SwiftUI: как я могу уловить изменение значения наблюдаемого объекта при выполнении функции
У меня проблема с наблюдаемым объектом в SwiftUI. Я вижу изменяющиеся значения наблюдаемого объекта в структуре View. Однако в классе или функции, даже если я изменю текстовое значение TextField(который является наблюдаемым объектом), но "self.codeTwo.text" все равно не изменился.
вот мой образец кода (это мой ObservableObject)
class settingCodeTwo: ObservableObject {
private static let userDefaultTextKey = "textKey2"
@Published var text: String = UserDefaults.standard.string(forKey: settingCodeTwo.userDefaultTextKey) ?? ""
private var canc: AnyCancellable!
init() {
canc = $text.debounce(for: 0.2, scheduler: DispatchQueue.main).sink { newText in
UserDefaults.standard.set(newText, forKey: settingCodeTwo.userDefaultTextKey)
}
}
deinit {
canc.cancel()
}
}
и главная проблема... "self.codeTwo.text" никогда не менялся!
class NetworkManager: ObservableObject {
@ObservedObject var codeTwo = settingCodeTwo()
@Published var posts = [Post]()
func fetchData() {
var urlComponents = URLComponents()
urlComponents.scheme = "http"
urlComponents.host = "\(self.codeTwo.text)" //This one I want to use observable object
urlComponents.path = "/mob_json/mob_json.aspx"
urlComponents.queryItems = [
URLQueryItem(name: "nm_sp", value: "UP_MOB_CHECK_LOGIN"),
URLQueryItem(name: "param", value: "1000|1000|\(Gpass.hahaha)")
]
if let url = urlComponents.url {
print(url)
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error == nil {
let decoder = JSONDecoder()
if let safeData = data {
do {
let results = try decoder.decode(Results.self, from: safeData)
DispatchQueue.main.async {
self.posts = results.Table
}
} catch {
print(error)
}
}
}
}
task.resume()
}
}
}
и это вид, я могу уловить изменение значения в этом
import SwiftUI
import Combine
struct SettingView: View {
@ObservedObject var codeTwo = settingCodeTwo()
var body: some View {
ZStack {
Rectangle().foregroundColor(Color.white).edgesIgnoringSafeArea(.all).background(Color.white)
VStack {
TextField("test", text: $codeTwo.text).textFieldStyle(BottomLineTextFieldStyle()).foregroundColor(.blue)
Text(codeTwo.text)
}
}
}
}
Помоги мне, пожалуйста.
2 ответа
Используются два разных экземпляраSettingCodeTwo
- один в NetworkNamager
другой в SettingsView
, поэтому они не синхронизируются, если создаются одновременно.
Вот способ самосинхронизации этих двух экземпляров (это возможно, потому что они используют одно и то же хранилище - UserDefaults
)
Протестировано с Xcode 11.4 / iOS 13.4
Измененный код ниже (см. Также важные комментарии в строке)
extension UserDefaults {
@objc dynamic var textKey2: String { // helper keypath
return string(forKey: "textKey2") ?? ""
}
}
class SettingCodeTwo: ObservableObject { // use capitalised name for class !!!
private static let userDefaultTextKey = "textKey2"
@Published var text: String = UserDefaults.standard.string(forKey: SettingCodeTwo.userDefaultTextKey) ?? ""
private var canc: AnyCancellable!
private var observer: NSKeyValueObservation!
init() {
canc = $text.debounce(for: 0.2, scheduler: DispatchQueue.main).sink { newText in
UserDefaults.standard.set(newText, forKey: SettingCodeTwo.userDefaultTextKey)
}
observer = UserDefaults.standard.observe(\.textKey2, options: [.new]) { _, value in
if let newValue = value.newValue, self.text != newValue { // << avoid cycling on changed self
self.text = newValue
}
}
}
}
class NetworkManager: ObservableObject {
var codeTwo = SettingCodeTwo() // no @ObservedObject needed here
...
Код, отличный от SwiftUI
- Использовать
ObservedObject
только для SwiftUI ваша функция / другой код, отличный от SwiftUI, не будет реагировать на изменения. - Используйте подписчика как
Sink
наблюдать за изменениями любого издателя. (Каждый@Published
переменная имеет издателя в качестве обернутого значения, вы можете использовать его, добавив префикс$
подписать.
Причина для SwiftUI
Просмотр не реагирует на изменение свойств класса:
struct
является типом значения, поэтому при изменении любого из его свойств значениеstruct
изменилосьclass
является ссылочным типом, при изменении любого из его свойств базовыйclass
экземпляр все тот же.- Если вы назначите новый экземпляр класса, вы заметите, что представление реагирует на изменение.
Подходить:
- Используйте отдельное представление, и оно принимает
codeTwoText
как@Binding
таким образом, когдаcodeTwoText
изменяет представление, которое будет обновлено, чтобы отразить новое значение. - Вы можете оставить модель как класс, чтобы никаких изменений в ней не происходило.
пример
class Model : ObservableObject {
@Published var name : String //Ensure the property is `Published`.
init(name: String) {
self.name = name
}
}
struct NameView : View {
@Binding var name : String
var body: some View {
return Text(name)
}
}
struct ContentView: View {
@ObservedObject var model : Model
var body: some View {
VStack {
Text("Hello, World!")
NameView(name: $model.name) //Passing the Binding to name
}
}
}
Тестирование
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
let model = Model(name: "aaa")
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
model.name = "bbb"
}
return ContentView(model: model)
}
}