繁体   English   中英

SwiftUI:当 state 变量更改时,视图不会更新

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM