[英]SwiftUI: View does not update when state variable changes
这个示例代码有什么问题吗? Text
视图以一个字符延迟更新。 例如,如果我在文本字段中输入“123”, Text
视图将显示“12”。
如果我用简单的结构替换contacts
并更改其givenName
属性,则视图会正确更新。
请注意, print
语句确实打印正确(即,如果您键入“123”,它会打印“1”,然后是“12”,然后是“123”。因此, contacts.givenName
确实得到了应有的更新。
我看到了其他类似标题的问题,但是这段代码似乎没有我看到的任何问题中描述的问题。
import SwiftUI
import Contacts
struct ContentView: View {
@State var name: String = ""
@State var contact = CNMutableContact()
var body: some View {
TextField("name", text: $name)
.onChange(of: name) { newValue in
contact.givenName = newValue
print("contact.givenName = \(contact.givenName)")
}
Text("contact.givenName = \(contact.givenName)")
}
}
更新:我在文本视图中添加了一个id
,并在我更新contact
state 变量时增加它。 它不漂亮,但它有效。 其他解决方案似乎过于复杂,不应该如此复杂。
struct ContentView: View { @State var name: String = "" @State var contact = CNMutableContact() @State var viewID = 0 // 改变这个以使视图集中更新
var body: some View {
TextField("name", text: $name)
.padding()
.onChange(of: name) { newValue in
contact.givenName = newValue
print("contact.givenName = \(contact.givenName)")
viewID += 1 // force the Text view to update
}
Text("contact.givenName = \(contact.givenName)").id(viewID)
}
}
造成这种情况的原因是您的CNMutableContact
@State
@State
最适用于值类型 - 每当为属性分配新值时,它都会告诉View
重新渲染。 但是,在您的情况下, CNMutableContact
是一个引用类型。 因此,您不是在设置一个真正的新值,而是在修改一个已经存在的值。 在这种情况下, View
仅在name
更改时更新,然后触发您的onChange
,因此在contact
更改后没有更新,您总是落后一步。
但是,您需要@State
类的东西,否则您无法更改联系人。
有几个解决方案。 我认为最简单的方法是将CNMutableContact
包装在ObservableObject
中,并在更改其属性时显式调用objectWillChange.send()
。 这样, View
将被重新渲染(即使上面没有任何@Published
属性)。
class ContactViewModel : ObservableObject {
var contact = CNMutableContact()
func changeGivenName(_ newValue : String) {
contact.givenName = newValue
self.objectWillChange.send()
}
}
struct ContentView: View {
@State var name: String = ""
@StateObject private var contactVM = ContactViewModel()
var body: some View {
TextField("name", text: $name)
.onChange(of: name) { newValue in
contactVM.changeGivenName(newValue)
print("contact.givenName = \(contactVM.contact.givenName)")
}
Text("contact.givenName = \(contactVM.contact.givenName)")
}
}
另一种选择是将name
移动到视图 model 并使用组合来观察更改。 这在没有objectWillChange
的情况下有效,因为接收sink
会在name
更改时在同一运行循环上更新contact
,因此@Published
属性包装器会在对contact
进行更改后通知View
进行更新。
import Combine
import SwiftUI
import Contacts
class ContactViewModel : ObservableObject {
@Published var name: String = ""
var contact = CNMutableContact()
private var cancellable : AnyCancellable?
init() {
cancellable = $name.sink {
self.contact.givenName = $0
}
}
}
struct ContentView: View {
@StateObject private var contactVM = ContactViewModel()
var body: some View {
TextField("name", text: $contactVM.name)
Text("contact.givenName = \(contactVM.contact.givenName)")
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.