尝试保存用户设置,但 UserDefaults 不起作用,Xcode 12.3,swiftui 2.0,当我重新加载我的应用程序时,我的设置没有更新为新值)
class PrayerTimeViewModel: ObservableObject {
@Published var lm = LocationManager()
@Published var method: CalculationMethod = .dubai {
didSet {
UserDefaults.standard.set(method.params, forKey: "method")
self.getPrayerTime()
}
}
func getPrayerTime() {
let cal = Calendar(identifier: Calendar.Identifier.gregorian)
let date = cal.dateComponents([.year, .month, .day], from: Date())
let coordinates = Coordinates(latitude: lm.location?.latitude ?? 0.0, longitude: lm.location?.longitude ?? 0.0)
var par = method.params
par.madhab = mashab
self.times = PrayerTimes(coordinates: coordinates, date: date, calculationParameters: par)
}
并查看...使用 AppStorage 更新
struct MethodView: View {
@ObservedObject var model: PrayerTimeViewModel
@Environment(\.presentationMode) var presentationMode
@AppStorage("method", store: UserDefaults(suiteName: "method")) var method: CalculationMethod = .dubai
var body: some View {
List(CalculationMethod.allCases, id: \.self) { item in
Button(action: {
self.model.objectWillChange.send()
self.presentationMode.wrappedValue.dismiss()
self.model.method = item
method = item
}) {
HStack {
Text("\(item.rawValue)")
if model.method == item {
Image(systemName: "checkmark")
.foregroundColor(.black)
}
}
}
}
}
}
答案 0 :(得分:1)
你有两个问题。
首先,正如我在上面的评论中提到的,您正在为 UserDefaults
使用两个不同的套件。这意味着您正在从两个不同的位置进行存储和检索。使用 UserDefaults.standard
或与您选择的套件 UserDefaults(suitName: "method")
一起使用 - 您不必使用套件,除非您计划与其他扩展共享您的默认设置,那么这样做是明智的。< /p>
其次,您在 UserDefaults
中存储了错误的项目。您正在存储计算属性 params
而不是实际的枚举值。当您尝试检索该值时,它会失败,因为它没有获得预期的值并使用您设置的默认值。
这是一个简单的例子,展示了你可以做什么。有一个简单的枚举,它具有原始值 (String
) 并符合 Codable
,它还具有计算属性。这与您的枚举匹配。
我已将初始化程序添加到我的 ObservableObject
。这用于在构造 Place
对象时从 UserDefaults 填充我发布的 Race
。
然后在我的 ContentView
中,我根据按下的按钮更新 place
。这会更新 UI 并更新 UserDefaults
中的值。
这应该足以让您了解它是如何工作的。
enum Place: String, Codable {
case first
case second
case third
case notPlaced
var someComputedProperty: String {
"Value stored: \(self.rawValue)"
}
}
class Race: ObservableObject {
@Published var place: Place = .notPlaced {
didSet {
// Store the rawValue of the enum into UserDefaults
// We can store the actual enum but that requires more code
UserDefaults.standard.setValue(place.rawValue, forKey: "method")
// Using a custom suite
// UserDefaults(suiteName: "method").setValue(place.rawValue, forKey: "method")
}
}
init() {
// Load the value from UserDefaults if it exists
if let rawValue = UserDefaults.standard.string(forKey: "method") {
// We need to nil-coalesce here as this is a failable initializer
self.place = Place(rawValue: rawValue) ?? .notPlaced
}
// Using a custom suite
// if let rawValue = UserDefaults(suiteName: "method")?.string(forKey: "method") {
// self.place = Place(rawValue: rawValue) ?? .notPlaced
// }
}
}
struct ContentView: View {
@StateObject var race: Race = Race()
var body: some View {
VStack(spacing: 20) {
Text(race.place.someComputedProperty)
.padding(.bottom, 20)
Button("Set First") {
race.place = .first
}
Button("Set Second") {
race.place = .second
}
Button("Set Third") {
race.place = .third
}
}
}
}
因为枚举符合 Codable
,所以可以使用 AppStorage
来读取和写入属性。但是,这不会更新您的 ObservableObject
中的值,因此它们很容易不同步。最好有一个地方可以控制一个值。在这种情况下,您的 ObservableObject
应该是事实的来源,并且所有更新(对 UserDefaults
的读取和写入)都应该通过那里进行。
答案 1 :(得分:0)
您在一个 UserDefaults 域中写入但从不同的域中读取。假设您的意图是仅使用套件 UserDefaults,您应该在模型中更改一个,例如
@Published var method: CalculationMethod = .dubai {
didSet {
UserDefaults(suiteName: "method").set(method.params, forKey: "method")
self.getPrayerTime()
}
}
或者如果您想使用标准,那么只需使用带有默认构造函数的 AppStorage,例如
// use UserDefaults.standard by default
@AppStorage("method") var method: CalculationMethod = .dubai