swift - 通过 @Published var 上的 didSet 将日期/时间保存到 UserDefaults(来自 SwiftUI 的 TimePicker 组件)
问题描述
我正在尝试保存用户的签到时间偏好(向他们发送通知提醒)。到目前为止,我已经能够将 Bool 和字符串保存到 UserDefaults 但我无法弄清楚如何保存时间偏好。这是我的 UserSettings 类来存储首选项。
public class UserSettings: ObservableObject {
@Published var eveningCheckin: Bool {
didSet{
UserDefaults.standard.set(eveningCheckin, forKey: "eveningCheckin")
print("Evening checkin toggle value in didSET to \(self.eveningCheckin)")
}
}
@Published var eveningCheckinTime: Date {
didSet{
UserDefaults.standard.set(eveningCheckinTime, forKey: "eveningCheckinTime")
print("Evening checkin didSet to \(self.eveningCheckinTime)")
}
}
init() {
self.eveningCheckin = UserDefaults.standard.object(forKey: "eveningCheckin") as? Bool ?? false
self.eveningCheckinTime = UserDefaults.standard.object(forKey: "eveningCheckinTime") as? Date ?? Date(timeIntervalSince1970: 64800)
print("Evening checkin time init to \(self.eveningCheckinTime)")// --> To debug
print("Evening checkin toggle value in init to \(self.eveningCheckin)")
}
}
我想使用 SwiftUI 提供的这样的时间选择器来设置这个晚上的签到时间。这是我的设置和时间选择器视图。
import SwiftUI
struct SettingsMain: View {
@ObservedObject var userSettings = UserSettings()
@State private var showTimepickerEvening = false
var body: some View {
NavigationView{
ScrollView {
VStack {
//Evening checkins
Button(action: {
//Show Evening time picker sheet
self.showTimepickerEvening.toggle()
}) {
HStack {
Text("\(userSettings.eveningCheckinTime.hour12):\(userSettings.eveningCheckinTime.minute0x) \(userSettings.eveningCheckinTime.amPM.lowercased()) ")
Text("Change >")
}
}
.sheet(isPresented: $showTimepickerEvening) {
//Sheet view with the Timepicker
TimePickerView(pickedTime: self.$userSettings.eveningCheckinTime)
}
}
.buttonStyle(PlainButtonStyle())
}
.navigationBarTitle("Preferences", displayMode: .inline)
}
}
}
struct TimePickerView: View {
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
@Binding var pickedTime: Date
var body: some View {
VStack {
DatePicker("Checkin time", selection: $pickedTime, displayedComponents: .hourAndMinute)
.labelsHidden()
Button(action: {
//Dimiss. Should I actually update my UserDefaults here as well?
self.presentationMode.wrappedValue.dismiss()
}) {
ZStack {
ColorManager.buttonGrey
Text("Save time")
.font(.system(size: 20))
.fontWeight(.semibold)
}
.frame(height: 64)
}
.padding(.all)
.buttonStyle(PlainButtonStyle())
}
.background(Color.white)
}
}
我的问题是时间选择器确实将时间设置到 @Published var 但它会被 init 语句重置。布尔值不会发生这种情况。为什么??
这是上面 print() 语句的输出:基本上意味着每次我尝试使用 Datepicker 设置时间 -> didSet 确实选择了新时间,但 init() 将其重置为默认值。
晚上签到设置为 1970-01-01 12:12:00 +0000
晚上入住时间开始到 1970-01-01 18:00:00 +0000
晚上签入将 init 中的值切换为 true
晚上签到设置为 1970-01-01 12:13:00 +0000
晚上入住时间开始到 1970-01-01 18:00:00 +0000
晚上签入将 init 中的值切换为 true
解决方案
您如何存储和注释视图模型的问题。您永远不应该@ObservedObject
在View
本身中创建一个,而是注入它。每当@ObservedObject
' 的@Published
属性发生变化时,View
存储该对象的 将被重新加载 - 这意味着如果您在@ObservedObject
内部初始化它View
,将创建该对象的新实例。
您需要将对象注入到您的视图中,以避免每次刷新视图时都重新创建它。
struct SettingsMain: View {
@ObservedObject var userSettings: UserSettings
@State private var showTimepickerEvening = false
...
从你创建的任何地方SettingsMain
(我们称之为MainView
插图),UserSettings
在那里创建并注释它@State
(或者如果你是从一个以外的东西创建它View
- 比如说一个 ViewModel,然后让它@Published
):
struct MainView: View {
@State var userSettings = UserSettings()
var body: some View {
SettingsMain(userSettings: userSettings)
}
}
推荐阅读
- ruby - Firebird 无法保存到 blob 列,因为字符串文字太大
- swift - 处理 UrlSession 任务中的 NotConnectedToInternet 错误
- c++ - C++ 使用哪个容器来存储缓存内存
- swift - 将 UIImage 裁剪为遮罩
- c# - 如何为嵌套集合发送 ItemControl 项的位置
- java - MalformedStreamException: Stream ended unexpectedly
- java - 尝试使用分号将数据导出到 CSV
- mysql - 准备语句循环行 MySQL
- python - 在 Python 中使用 ElementTree 从 xml 中获取数据
- bluetooth-lowenergy - 在 xcode 应用程序中实现蓝牙 LE 的问题