【问题标题】:SwiftUI, how to bind EnvironmnetObject Int property to TextField?SwiftUI,如何将 EnvironmnetObject Int 属性绑定到 TextField?
【发布时间】:2020-04-19 17:54:33
【问题描述】:

我有一个 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. 与第一个问题相关的是,一旦值更改,我将如何接收事件,我尝试了以下 sn-p,但没有任何内容被触发(我假设是因为文本字段未正确绑定...
$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 才能传播更改,这不是我想要的,我希望每次更改字段时都传播更改...

【问题讨论】:

    标签: ios swift macos swiftui combine


    【解决方案1】:

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

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

    添加无效字符时会有一点延迟,但这是最干净的解决方案,不允许在不重置 TextField 状态的情况下添加无效字符。

    它还可以立即提交更改,而无需等待用户按 Enter 键或无需等待字段模糊。

    【讨论】:

      【解决方案2】:

      这样做,您甚至不必按 Enter。如果您将 Store() 放在 SceneDelegate 中,这也适用于 EnvironmentObject:

      struct ContentView: View {
      @ObservedObject var store = Store()
      
      
      var body: some View {
          VStack {
              TextField("Fetch interval", text: $store.fetchInterval)
          Text("\(store.fetchInterval)")
          }
      } }
      

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

      【讨论】:

      • 嗯,是的,只是使用文本直接改变观察值,但验证呢?它只会尝试将文本放在那里对吗?
      • 如果您只在 iPhone 上使用您的应用程序,最简单的解决方案是在您的 TextField 下添加“.keyboardType(.numberPad)”。请注意,在模拟器上,您必须在单击 TextField 后打开小键盘 (CMD+K) 才能正确模拟此行为。如需更复杂的解决方案,请查看:stackoverflow.com/questions/58733003/…
      • 不,不幸的是(?)我正在开发一个 mac 应用程序,所以允许文本值更让人头疼
      • 此代码无法编译。 TextField 将 Binding 作为参数。它不需要 Int。
      【解决方案3】:

      如何在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: $0) {
                                 self.store.fetchInterval = value.intValue
                             }}))
                     Text("\(store.fetchInterval)").padding()
                 }
           }
      }
      

      【讨论】:

      • 我确实最终选择了类似的东西,谢谢
      猜你喜欢
      • 1970-01-01
      • 2018-05-13
      • 2011-01-13
      • 1970-01-01
      • 1970-01-01
      • 2020-04-25
      • 1970-01-01
      • 1970-01-01
      • 2017-05-26
      相关资源
      最近更新 更多