SwiftUI, как связать свойство EnvironmnetObject Int с TextField? - PullRequest
1 голос
/ 19 апреля 2020

У меня есть ObservableObject, который должен содержать состояние моего приложения:

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

Теперь этот объект внедряется в root моей иерархии, а затем в какой-то компонент вниз по дереву. пытаюсь получить к нему доступ и привязать его к 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, она не обновляет переменную правильно

Edit 2: Ok I только что обнаружили, что недостаточно только изменить поле, нужно нажать Enter, чтобы изменение распространялось, а это не то, чего я хочу, я хочу, чтобы изменения распространялись каждый раз, когда поле изменяется ...

Ответы [ 3 ]

1 голос
/ 20 апреля 2020

как насчет простого решения, которое также хорошо работает на 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()
           }
     }
}
1 голос
/ 19 апреля 2020

Сделайте это так, и вам даже не придется нажимать ввод. Это также будет работать с 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 представление всегда обновляется автоматически, если изменяется переменная в нем.

0 голосов
/ 20 апреля 2020

Для всех, кого это интересует, это решение, которое я выбрал:

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

Существует небольшая задержка при добавлении недопустимого символа, но это самое чистое решение, которое не разрешить недопустимые символы без необходимости сбрасывать состояние TextField.

Он также сразу же фиксирует изменения, не дожидаясь нажатия клавиши ввода пользователем или не дожидаясь размытия поля.

...