您的位置:首页 > 移动开发 > Swift

用SwiftUI构建表单

2019-08-09 16:45 2126 查看
原文链接:https://mecid.github.io/2019/06/19/building-forms-with-swiftui/

原文:Building forms with SwiftUI

19 Jun 2019

在苹果最新发布的 Xcode Beta 2 中更新了 SwiftUI。在 WWDC 大会中演示的这个版本包含了 Form 组件。今天我们就使用 SwiftUI 来构建一个表单类型的布局。我将演示一个真正的用 SwiftUI 的新 Form 组件编写的设置界面。

我在编写一个睡眠记录 app,它需要有一个设置界面。在这个界面包含几个开关——用于开启或关闭某些特性,几颗按钮——用于应用内购的,以及一个选择器——用于选择睡眠记录的敏感级别。

BindableObject 类

首先创建设置界面中将会使用到的 BindableObject 类。在前一篇博客中,我介绍过这个 BindableObject,关于它的使用,你可以参考这里

import SwiftUI
import Combine

class SettingsStore: BindableObject {
var didChange = PassthroughSubject<Void, Never>()

private enum Keys {
static let pro = "pro"
static let sleepGoal = "sleep_goal"
static let notificationEnabled = "notifications_enabled"
static let sleepTrackingEnabled = "sleep_tracking_enabled"
static let sleepTrackingMode = "sleep_tracking_mode"
}

private let cancellable: Cancellable
private let defaults: UserDefaults

init(defaults: UserDefaults = .standard) {
self.defaults = defaults

defaults.register(defaults: [
Keys.sleepGoal: 8,
Keys.sleepTrackingEnabled: true,
Keys.sleepTrackingMode: SleepTrackingMode.moderate.rawValue
])

cancellable = NotificationCenter.default
.publisher(for: UserDefaults.didChangeNotification)
.map { _ in () }
.subscribe(didChange)
}

var isNotificationEnabled: Bool {
set { defaults.set(newValue, forKey: Keys.notificationEnabled) }
get { defaults.bool(forKey: Keys.notificationEnabled) }
}

var isPro: Bool {
set { defaults.set(newValue, forKey: Keys.pro) }
get { defaults.bool(forKey: Keys.pro) }
}

var isSleepTrackingEnabled: Bool {
set { defaults.set(newValue, forKey: Keys.sleepTrackingEnabled) }
get { defaults.bool(forKey: Keys.sleepTrackingEnabled) }
}

var sleepGoal: Int {
set { defaults.set(newValue, forKey: Keys.sleepGoal) }
get { defaults.integer(forKey: Keys.sleepGoal) }
}

enum SleepTrackingMode: String, CaseIterable {
case low
case moderate
case aggressive
}

var sleepTrackingMode: SleepTrackingMode {
get {
return defaults.string(forKey: Keys.sleepTrackingMode)
.flatMap { SleepTrackingMode(rawValue: $0) } ?? .moderate
}

set {
defaults.set(newValue.rawValue, forKey: Keys.sleepTrackingMode)
}
}
}

extension SettingsStore {
func unlockPro() {
// You can do your in-app transactions here
isPro = true
}

func restorePurchase() {
// You can do you in-app purchase restore here
isPro = true
}
}

我们创建了一个简单的 SettingsStore 类,它实现了 BindableObject 协议。它只用实现一个 didChange 属性。SwiftUI 通过这个属性了解什么时候发生了改变,当有变化发生时,它会重新构造相关的 view。

另一个值得注意的地方是 Combine 框架的使用。我们使用 notification center publisher 来订阅 UserDefaults 的改变。当 UserDefault 的值发生了改变,我们会收到通知并调用 didChange 属性。后面我会在博客中介绍 Combine 框架。现在,你只需要知道 UserDefaults 中的任何改变都会向 didChange 属性发送 Void 值即可,这将触发视图的重构。

我们也可以不使用 NotificationCenter publisher,而是在 SettingsStore 的每一个属性的 setter 方法中调用 didChange 的 send 方法,但冗余代码就有点多了。

SettingsView

让我们开始构建 UI。我们会用到 Text、Toggle、Stepper、Picker 和 Button 控件。 SettingsView 的代码如下。

import SwiftUI

struct SettingsView: View {
@EnvironmentObject var settings: SettingsStore

var body: some View {
NavigationView {
Form {
Section(header: Text("Notifications settings")) {
Toggle(isOn: $settings.isNotificationEnabled) {
Text("Notification:")
}
}

Section(header: Text("Sleep tracking settings")) {
Toggle(isOn: $settings.isSleepTrackingEnabled) {
Text("Sleep tracking:")
}

Picker(
selection: $settings.sleepTrackingMode,
label: Text("Sleep tracking mode")
) {
ForEach(SettingsStore.SleepTrackingMode.allCases.identified(by: \.self)) {
Text($0.rawValue).tag($0)
}
}

Stepper(value: $settings.sleepGoal, in: 6...12) {
Text("Sleep goal is \(settings.sleepGoal) hours")
}
}

if !settings.isPro {
Section {
Button(action: {
self.settings.unlockPro()
}) {
Text("Unlock PRO")
}

Button(action: {
self.settings.restorePurchase()
}) {
Text("Restore purchase")
}
}
}
}
.navigationBarTitle(Text("Settings"))
}
}
}

你也看到了,我们将布局代码用 Form 组件包裹起来。Form 组件使用分组 List 来表示 cell 中的子控件。通过将布局包裹在 Form 中,SwiftUI 能够改变每个元素的可视化外观。你可以用 VStack 替换 Form,来看看二者有何不同。Picker 的外观也不一样。它用另外一个列表窗口来显示内容。我们无需做任何事情,一切都被封装得好好的。这就是声明式编程的真正威力。每个组件的外观都会有不同的适配方式,我们可以用其它容器来包裹它们,就可以轻易地改变它们的外观。下面是最终效果截图。

结论

SwiftUI 能够让构建 app 变得非常简单。你可以用 Form 组件将一个复杂的表单布局分成多个 section 并使用不同的数据源。希望你像我一样爱上 SwiftUI,接下来我会介绍更多关于 SwiftUI 的话题。关于本文的问题,请推我。谢谢,下周再见!

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: