Raw*_*ean 15 ios swift swiftui
这个示例代码有什么问题吗?视图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)")
}
}
Run Code Online (Sandbox Code Playgroud)
更新:
我id向文本视图添加了 ,并在更新contact状态变量时递增它。它不漂亮,但很有效。其他解决方案似乎过于复杂,不应该如此复杂。
struct ContentView: View {
@State var name: String = ""
@State var contact = CNMutableContact()
@State var viewID = 0 // change this to foce the view to update
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)
}
}
Run Code Online (Sandbox Code Playgroud)
jn_*_*pdx 14
其原因是用于@State您的CNMutableContact.
@State最适合值类型——每当将新值分配给属性时,它都会告诉View重新渲染。不过,就您而言,CNMutableContact是引用类型。因此,您并不是在设置真正的新值,而是在修改已经存在的值。在这种情况下,View唯一会在发生更改时更新name,然后触发您的onChange,因此更改后没有更新contact,您总是落后一步。
但是,您需要类似的东西@State,因为否则您无法改变联系人。
对此有几个解决方案。我认为最简单的方法是将您的包装CNMutableContact在 an 中,并在更改其属性时显式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)")
}
}
Run Code Online (Sandbox Code Playgroud)
另一种选择是转移name到视图模型并使用组合来观察变化。这是可行的objectWillChange,因为同一运行循环上的sink更新发生更改,因此属性包装器会在更改完成后发出更新信号。contactname@PublishedViewcontact
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)")
}
}
Run Code Online (Sandbox Code Playgroud)
小智 1
Text()您显示的字符比原始字符少一个的原因name:
onChange { }当您更新内部闭包时contact.givenName,它不会刷新/重新加载您的整个视图。因为您实际上并没有更新整个contact@State 变量,而是只是更新了@State 变量givenName的一个属性contact。
根据变量的规则@State,当您更新整个@State变量时,视图才会重新加载。
基于此我将分享2个更简单的解决方案:
contact解决方案1:完全更新@State变量对象
import SwiftUI
import Contacts
struct ContentView: View {
@State var name: String = ""
@State var contact = CNMutableContact()
var body: some View {
TextField("name", text: $name)
.padding()
.onChange(of: name) { oldValue, newValue in
contact = .init() // Updating the @State variable contact completely
contact.givenName = newValue
print("contact.givenName = \(contact.givenName)")
}
Text("contact.givenName = \(contact.givenName)")
}
}
Run Code Online (Sandbox Code Playgroud)
效果很好。但我个人不喜欢这个解决方案,因为它contact每次都会创建新的引用。所以我认为它效率不高。
解决方案 2:使用另一个 @State varcontactName来跟踪联系人姓名
import SwiftUI
import Contacts
struct ContentView: View {
@State var name: String = ""
@State var contactName: String = ""
var contact = CNMutableContact() // We don't need @State for this
var body: some View {
TextField("name", text: $name)
.padding()
.onChange(of: name) { oldValue, newValue in
contact.givenName = newValue
contactName = newValue
print("contact.givenName = \(contact.givenName)")
}
Text("contact.givenName = \(contactName)")
}
}
Run Code Online (Sandbox Code Playgroud)
别担心,您的意愿contact.givenName会相应更新。