在上一章节中,
我们已经使用 SwiftUI 的 Form
组件实现了一个设置界面(Settings),
并为其添加了排序、筛选等多种选项。
然而,当用户在设置界面选择了不同的偏好后,主列表并不会根据用户的设置进行动态更新。
本篇文章将继 续扩展我们的应用,通过 Combine 和 @EnvironmentObject 来实现数据共享, 使我们的设置与主列表能够联动更新。
在这篇文章中,我们将讨论以下几个重点:
- 如何使用 enum 来重构并简化代码
- 如何借助 UserDefaults 永久保存用户的偏好设置
- 如何使用 Combine 和 @EnvironmentObject 在视图之间共享数据、并实现自动刷新
一、使用 Enum 重构代码
目前,我们在界面中用数组存放了三种餐厅列表的显示顺序(如「Alphabetical」「Show Favorite First」「Show Check-in First」)。 虽然直接使用字符串数组没问题,但借助枚举可以使代码更简洁、可读性更好,也更符合类型安全的理念。
例如,可以在名为 DisplayOrderType
的枚举中包含所有与显示顺序相关的值,
并让其遵循 CaseIterable
以便我们可以遍历所有枚举值:
enum DisplayOrderType: Int, CaseIterable {
case alphabetical = 0
case favoriteFirst = 1
case checkInFirst = 2
init(type: Int) {
switch type {
case 0: self = .alphabetical
case 1: self = .favoriteFirst
case 2: self = .checkInFirst
default: self = .alphabetical
}
}
var text: String {
switch self {
case .alphabetical:
return "Alphabetical"
case .favoriteFirst:
return "Show Favorite First"
case .checkInFirst:
return "Show Check-in First"
}
}
}
完成后,你可以将枚举代码放在 SettingStore.swift
中,方便与后续设置相关的逻辑一起管理。
在 SettingView.swift
中,
将原先的 selectedOrder
改为 @State private var selectedOrder = DisplayOrderType.alphabetical
,
并在 Picker
中使用 ForEach(DisplayOrderType.allCases, id: \.self) { ... }
来展示所有可用的枚举值。
这样不仅让代码更整洁,也为后续扩展(比如增加新的排序方式)提供了更好的可维护性。
二、使用 UserDefaults 存储用户偏好设置
当用户重启应用后,如果我们的设置又恢复到默认值,显然并不理想。
为此,我们可以使用 iOS 内置的 UserDefaults
机制来将用户的设置以键值对的形式保存在本地。
在 SettingStore.swift
中创建一个 SettingStore
类,用于读写用户偏好:
final class SettingStore {
init() {
UserDefaults.standard.register(defaults: [
"view.preferences.showCheckInOnly": false,
"view.preferences.displayOrder": 0,
"view.preferences.maxPriceLevel": 5
])
}
var showCheckInOnly: Bool = UserDefaults.standard.bool(forKey: "view.preferences.showCheckInOnly") {
didSet {
UserDefaults.standard.set(showCheckInOnly, forKey: "view.preferences.showCheckInOnly")
}
}
var displayOrder: DisplayOrderType = DisplayOrderType(type: UserDefaults.standard.integer(forKey: "view.preferences.displayOrder")) {
didSet {
UserDefaults.standard.set(displayOrder.rawValue, forKey: "view.preferences.displayOrder")
}
}
var maxPriceLevel: Int = UserDefaults.standard.integer(forKey: "view.preferences.maxPriceLevel") {
didSet {
UserDefaults.standard.set(maxPriceLevel, forKey: "view.preferences.maxPriceLevel")
}
}
}
在 init
方法中,我们调用 register(defaults:)
方法为初次启动或未设置的键赋予默认值。
之后的每个属性都在 didSet
中使用 UserDefaults.standard.set(...)
来持续更新到本地,
让每次修改都即时保存。
接下来,在 SettingView.swift
中可以引入 SettingStore
,并在点击“保存”按钮后,
将当前的 UI 状态同步回 SettingStore
:
Button {
self.settingStore.showCheckInOnly = self.showCheckInOnly
self.settingStore.displayOrder = self.selectedOrder
self.settingStore.maxPriceLevel = self.maxPriceLevel
dismiss()
} label: {
Text("Save")
.foregroundStyle(.primary)
}
为了在视图出现时就加载用户保存的偏好,可以使用 .onAppear
修饰符:
.onAppear {
self.selectedOrder = self.settingStore.displayOrder
self.showCheckInOnly = self.settingStore.showCheckInOnly
self.maxPriceLevel = self.settingStore.maxPriceLevel
}
这样,每次打开 Settings 界面,都能自动将已保存的用户设置加载并展示出来。