无法使用 UseDefaults 的 propertyWrapper 为类型 'TextField<_>' 调用初始值设定项

Cannot invoke initializer for type 'TextField<_>' with propertyWrapper of UseDefaults

我正在处理更新 UserDefaults 中的多个值的 SwiftUI 屏幕,以允许应用保留基本设置。我正在尝试使用 Combine 和 SwiftUI,因为这是原生的 WatchOS 应用程序。

基本视图给我一个错误,我认为这与 UserDefaults 的 属性Wrapper 有关,但因为我从未使用过 属性Wrappers(或 Combine)我无法弄清楚如何解决这个问题。

这是 属性 包装器:

import Foundation

@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T

    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            UserDefaults(suiteName: "group.com.my.app")!.value(forKey: key) as? T ?? defaultValue
        } set {
            UserDefaults(suiteName: "group.com.my.app")!.set(newValue, forKey: key)
        }
    }
}

如您所见,它包装了 UserDefaults 的所有密钥对。

我的class同样非常简单,由两个布尔值和一个双精度值组成

import SwiftUI
import Foundation
import Combine

class Setup: ObservableObject {

    private var notificationSubscription: AnyCancellable?

    let objectWillChange = PassthroughSubject<Setup,Never>()

    @UserDefault("keyOpt1Enabled", defaultValue: false)
    var opt1Enabled: Bool {
        willSet{
            objectWillChange.send(self)
        }
    }

    @UserDefault("keyOpt2Enabled", defaultValue: false)
    var opt2Enabled: Bool {
        willSet{
            objectWillChange.send(self)
        }
    }

    @UserDefault("keyValueDouble", defaultValue: Double(0.00))
    var someValueDouble: Double {
        willSet{
            objectWillChange.send(self)
        }

    }
    init() {

        notificationSubscription = NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification).sink { _ in
                   self.objectWillChange.send(self)
        }
    }
}

问题是在 SwiftUI 中我使用 TextField 来允许输入和更新双精度值


    @ObservedObject var setup: Setup = Setup()

    private var currencyFormatter: NumberFormatter = {
        let f = NumberFormatter()
        f.numberStyle = .currency
        return f
    }()


    var body: some View {
        ScrollView{
            HStack{
                TextField(self.$setup.someValueDouble,
                          formatter: currencyFormatter,
                          placeholder: "0.00",
                          onEditingChanged: {_ in
                            print("editing changed")
                          },
                          onCommit: {
                            print("updated")
                            }
                )
            }

            HStack{
                Button(action: {
                    self.setup.opt1Enabled = false
                    self.setup.opt2Enabled = true
                } ) {
                    Text(verbatim: "Opt 1")
                        .font(Font.system(size: 16, design: Font.Design.rounded))
                }
                .background(setup.opt1Enabled ? Color.blue : Color.gray)
                .disabled(self.setup.opt1Enabled)
                .cornerRadius(5)

                Button(action: {
                    self.setup.opt1Enabled = true
                    self.setup.opt2Enabled = false
                }) {
                    Text(verbatim: "Opt 2")
                }
                .background(setup.opt2Enabled ? Color.blue : Color.gray)
                .disabled(self.setup.opt2Enabled)
            }
        }
    }
}

TextField 然后给出无法推断通用参数 'Label' 的消息。 Xcode 向 "Fix" 提供了这个,但最终结果是 TextField,这显然是不完整的,但整个程序中唯一的视图是 "ContentView",它是无效的。

问题是 TextField 的初始化程序首先将 String 作为占位符,然后是它绑定的值,因此它应该大致如下所示:

 TextField("Placeholder",
           value: self.$setup.someValueDouble,
           formatter: currencyFormatter,
           onEditingChanged: { _ in },
           onCommit:{})
     .keyboardType(.numberPad)

您可能想阅读有关 TextFields 和 Formatter 的更多信息,因为这似乎是一个棘手的主题。