SwiftUI 中的计算 (NSObject) 属性不会更新视图



所以,我想有一个Text,根据我的CoreData模型的内容改变其内容。为此,我在 Xcode beta 4 中使用了一个计算属性,但它似乎不再起作用了。要么这是一个错误,要么还有其他我没有看到的问题?

我遇到的问题是,当我的商店中调用 View 时,我的视图(和计算属性)似乎没有更新self.objectWillChange.send()

我还尝试将我的 var "导出"到商店并从那里获取,结果相同......


编辑:我刚刚在另一个类上尝试了相同的方法,它不仅适用于objectWillChange.send(),但仅适用于@Published,如果该类继承自 NSObject...


我刚刚发现:与

struct Today: View {
@EnvironmentObject var myStore: DateStore
var hasPlans: Bool {
guard let plans = myStore.getPlans() else { return false }
return plans.isEmpty
}
var body: some View{
VStack{
Text(hasPlans ? "I have plans":"I have time today")
Button(action: {
self.myStore.addPlans(for: Date())
}) {
Text("I have plans")
}
}
}
class DateStore: NSObject, ObservableObject, NSFetchedResultsControllerDelegate {
private var fetchedResultsController: NSFetchedResultsController<DateStore>
//...
public func addPlans(for date: Date){
//{...}
if let day = self.dates.first(where: { $0.date == date}){
day.plans += 1
saveChanges()
}else{
self.create(date: dayDate)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.objectWillChange.send()
}
}

这是我的问题的一个非常简化的版本,我知道我的 DataModel 有效,因为值更改并调用了self.objectWillChange.send(),但我的视图由于某种原因没有更新......

我没有看到NSObject是问题的根源。问题似乎是您尚未实现objectWillChange。编译器会让你侥幸逃脱,但结果是你的objectWillChange什么都不做。

下面是一个简单的示例,演示如何使用绑定有效的计算属性配置 ObservableObject(即 NSObject):

class Thing : NSObject, ObservableObject {
let objectWillChange = ObservableObjectPublisher()
var computedProperty : Bool = true {
willSet {
self.objectWillChange.send()
}
}
}
struct ContentView: View {
@EnvironmentObject var thing : Thing
var body: some View {
VStack {
Button(self.thing.computedProperty ? "Hello" : "Goodbye") {
self.thing.computedProperty.toggle()
}
Toggle.init("", isOn: self.$thing.computedProperty).frame(width:0)
}
}
}

您可以通过点击按钮和开关看到一切都是实时的,并且响应视图中的绑定。

试验我自己的代码表现出类似的问题。

看起来 SwiftUI @Publisher魔术在采用 ObservableObject 的类是 NSObject 的子类时中断。

简短的答案是,如果当它不是 NSObject 的子类时,您可以让它与 @Published 一起工作,那么当您使其成为 NSObject 的子类时,请@Published替换为

let objectWillChange = PassthroughSubject<Void, Never>()

您必须在类文件中导入 Combine 框架才能执行此操作。

这是我正在试验的工作应用程序的一些代码。

观点:

import SwiftUI
struct ContentView: View {
//Bind using @ObservedObject
@ObservedObject var provider: Provider
var body: some View {
Text("(self.provider.distance)")
}
...
}

班级:

import Combine
class Provider: NSObject, ObservableObject {
//Instead of using @Published, use objectwillchange
//@Published var distance: CLLocationDistance = 0.0
let objectWillChange = PassthroughSubject<Void, Never>()
var distance = 0.0
...
func calculateDistance() {
...
// publish the change
self.objectWillChange.send()
self.distance = newDistance
}
}

一种有效的解决方案是简单地创建一个@State var,而不是使用计算属性。然而,根据 WWDC 关于 SwiftUI 的讨论,这感觉有些错误,因为我的"实际状态"存在于我的数据模型中,通过声明一个新的@State我需要让两者保持同步,这与"单一事实来源"模式背道而驰,不是吗?

最新更新