译自 www.hackingwithswift.com/books/ios-s…
更多内容,欢迎关注公众号 「Swift花园」
喜欢文章?不如来个 🔺💛➕三连?关注专栏,关注我 🚀🚀🚀
利用上下文添加选项
我们需要一个能够互相移动已联络过的人和未联络过的人的手段。最简单的做法是添加一个上下文菜单到 ProspectsView 的 VStack 上,让用户可以长按人员列表的人,触发菜单,然后点击选项把人在 tab 间移动。
现在,要把这个视图共享到三个地方,所以我们需要确保这个上下文菜单不管在哪里显示看起来都正确。最简单的选项是用三元操作符来设置按钮的标题:
.contextMenu {
Button(prospect.isContacted ? "Mark Uncontacted" : "Mark Contacted" ) {
prospect.isContacted.toggle()
}
}
虽然按钮更新了布尔状态,但并不会更新 UI。
之所以会这样是因为 Prospects 的 people 数组被标记了 @Published,当我们给数组添加或者删除项时会发出通知。但是,如果我们只是改变数组中的项,SwiftUI 无法检测到变化,因此没有视图会被刷新。
为了解决这个问题,我们需要手工告诉 SwiftUI 有某些重要的东西已经改变了。因此,相比只是反转 ProspectsView 的布尔状态,我们还要调用一个方法,在反转布尔值的同时发送一个改变通知。
把下面这个方法添加到 Prospects 类:
func toggle(_ prospect: Prospect) {
objectWillChange.send()
prospect.isContacted.toggle()
}
重要: 你应该在改变属性之前调用 objectWillChange.send(),确保 SwiftUI 正确执行动画。
现在,把 prospect.isContacted.toggle() 替换为:
self.prospects.toggle(prospect)
运行应用 —— 扫描一个用户,然后调起上下文菜单,点击动作,看着用户在已联络的人和未联络的人之间移动。
如你所见,直接修改 isContacted 会导致问题,因为尽管布尔值改变了,UI 不会更新。如果我们让代码不变,很有可能会遗忘这个问题,并且在其他地方反转布尔值,以致于引入 bug。
Swift 可以通过防止我们从 Prospects.swift 外部修改布尔值来缓和这个问题。有一个专门的访问控制符叫 fileprivate,它的意思是 “这个属性只能被当前文件的代码访问。” 当然,我们还是需要读取那个属性,所以我们诉诸另一个 Swift 特性:fileprivate(set),它的意思是 “这个属性可以在任何地方被读取,但只能从当前文件写入” —— 通过这个组合,我们能够确保某个布尔值可以被安全地使用。
把 Prospect 里的 isContacted 修改成这样:
fileprivate(set) var isContacted = false
这个改动并没有直接影响当前工程,但的确有助于确保未来的安全。如果你之前好奇过为什么我们把 Prospect 和 Prospects 类都放在同一个文件里,现在你知道原因了吧!
我的公众号 这里有Swift及计算机编程的相关文章,以及优秀国外文章翻译,欢迎关注~