SwiftUI,如何将 EnvironmnetObject Int 属性 绑定到 TextField?

SwiftUI, how to bind EnvironmnetObject Int property to TextField?

我有一个应该保存我的应用程序状态的 ObservableObject:

final class Store: ObservableObject {
  @Published var fetchInterval = 30
}

现在,该对象被注入到我的层次结构的根部,然后在树下的某个组件中我试图访问它并将其绑定到 TextField,即:

struct ConfigurationView: View {
  @EnvironmnetObject var store: Store

  var body: some View {
    TextField("Fetch interval", $store.fetchInterval, formatter: NumberFormatter())
    Text("\(store.fetchInterval)"
  }
}
  1. 即使变量已绑定(使用 $),属性 未更新,初始值显示正确但当我更改它时,文本字段更改但绑定是未传播
  2. 与第一个问题相关的是,值更改后我将如何接收事件,我尝试了以下代码片段,但没有触发任何内容(我假设是因为文本字段未正确绑定...
$fetchInterval
           .debounce(for: 0.8, scheduler: RunLoop.main)
           .removeDuplicates()
           .sink { interval in
               print("sink from my code \(interval)")
           }

非常感谢任何帮助。

编辑:我刚刚发现对于文本变量,绑定开箱即用,例如:

// on store
@Published var testString = "ropo"

// on component
TextField("Ropo", text: $store.testString)
Text("\(store.testString)")

只是在int字段上没有正确更新变量

编辑 2: 好的,我刚刚发现仅更改字段是不够的,必须按 Enter 才能传播更改,这不是我想要的,我希望每次更改字段时传播更改.. .

像这样操作,您甚至不必按回车键。这也适用于 EnvironmentObject,如果您将 Store() 放在 SceneDelegate 中:

struct ContentView: View {
@ObservedObject var store = Store()


var body: some View {
    VStack {
        TextField("Fetch interval", text: $store.fetchInterval)
    Text("\(store.fetchInterval)")
    }
} }

关于您的第二个问题:在 SwiftUI 中,如果其中的变量发生变化,视图总是会自动更新。

一个同样适用于 macos 的简单解决方案如何,如下所示:

import SwiftUI

final class Store: ObservableObject {
    @Published var fetchInterval: Int = 30
    }

    struct ContentView: View {
       @ObservedObject var store = Store()
       var body: some View {
           VStack{
               TextField("Fetch interval", text: Binding<String>(
                   get: { String(format: "%d", self.store.fetchInterval) },
                   set: {
                       if let value = NumberFormatter().number(from: [=10=]) {
                           self.store.fetchInterval = value.intValue
                       }}))
               Text("\(store.fetchInterval)").padding()
           }
     }
}

对于任何感兴趣的人,这是我最终得到的解决方案:

TextField("Seconds", text: Binding(
                    get: { String(self.store.fetchInterval) },
                    set: { self.store.fetchInterval = Int([=10=].filter { "0123456789".contains([=10=]) }) ?? self.store.fetchInterval }
                ))

添加无效字符时会有一小段延迟,但这是最简洁的解决方案,无需重置 TextField 的状态即可不允许无效字符。

它还会立即提交更改,而无需等待用户按下回车键或无需等待字段模糊。