SwiftUI 中的计算 (NSObject) 属性不会更新视图
Computed (NSObject) Properties in SwiftUI don't update the view
所以,我想要一个 Text
可以根据我的 CoreData 模型的内容更改其内容。为此,我在 Xcode beta 4 中使用了计算 属性,但它似乎不再起作用了。这是错误还是存在其他我没有发现的问题?
我遇到的问题是,当在我的商店中调用 self.objectWillChange.send()
时,我的视图(和计算的 属性)似乎没有得到更新。
我也尝试 'export' 我的 var 进入商店并从那里获取它,结果相同...
编辑:
我只是用另一个 class 尝试了同样的方法,它只对 objectWillChange.send()
不起作用,但只对 @Published
起作用,但是,如果 class 从 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: { [=10=].date == date}){
day.plans += 1
saveChanges()
}else{
self.create(date: dayDate)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.objectWillChange.send()
}
}
这是我的问题的一个非常简化的版本,我知道我的 DataModel 可以工作,因为值发生变化并且 self.objectWillChange.send()
被调用,但我的视图由于某种原因没有更新....
一个有效的解决方案是简单地制作一个 new @State var
而不是使用计算的 属性。然而,根据 WWDC 关于 SwiftUI 的讨论,这感觉有点不对劲,因为我的 'actual state' 存在于我的数据模型中,并且通过声明一个新的 @State
我需要使它们保持同步,这与 "Single Source of Truth" 模式不是吗?
用我自己的代码进行试验,表现出类似的问题。
当 Class 采用 ObservableObject 是 NSObject 的子类时,@Publisher SwiftUI 魔法似乎失效了。
简短的解决方案是,如果您可以在它不是 NSObject 的子类时使用 @Published,那么当您将它设为 NSObject 的子类时,将 @Published 替换为
let objectWillChange = PassthroughSubject<Void, Never>()
您必须在 Class 文件中导入 Combine 框架才能执行此操作。
这是我正在试验的工作应用程序中的一些代码。
观点:
import SwiftUI
struct ContentView: View {
//Bind using @ObservedObject
@ObservedObject var provider: Provider
var body: some View {
Text("\(self.provider.distance)")
}
...
}
Class:
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
}
}
我不认为 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)
}
}
}
您可以通过点击按钮和开关看到所有内容都处于活动状态并且响应视图中的绑定。
所以,我想要一个 Text
可以根据我的 CoreData 模型的内容更改其内容。为此,我在 Xcode beta 4 中使用了计算 属性,但它似乎不再起作用了。这是错误还是存在其他我没有发现的问题?
我遇到的问题是,当在我的商店中调用 self.objectWillChange.send()
时,我的视图(和计算的 属性)似乎没有得到更新。
我也尝试 'export' 我的 var 进入商店并从那里获取它,结果相同...
编辑:
我只是用另一个 class 尝试了同样的方法,它只对 objectWillChange.send()
不起作用,但只对 @Published
起作用,但是,如果 class 从 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: { [=10=].date == date}){
day.plans += 1
saveChanges()
}else{
self.create(date: dayDate)
}
}
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.objectWillChange.send()
}
}
这是我的问题的一个非常简化的版本,我知道我的 DataModel 可以工作,因为值发生变化并且 self.objectWillChange.send()
被调用,但我的视图由于某种原因没有更新....
一个有效的解决方案是简单地制作一个 new @State var
而不是使用计算的 属性。然而,根据 WWDC 关于 SwiftUI 的讨论,这感觉有点不对劲,因为我的 'actual state' 存在于我的数据模型中,并且通过声明一个新的 @State
我需要使它们保持同步,这与 "Single Source of Truth" 模式不是吗?
用我自己的代码进行试验,表现出类似的问题。
当 Class 采用 ObservableObject 是 NSObject 的子类时,@Publisher SwiftUI 魔法似乎失效了。
简短的解决方案是,如果您可以在它不是 NSObject 的子类时使用 @Published,那么当您将它设为 NSObject 的子类时,将 @Published 替换为
let objectWillChange = PassthroughSubject<Void, Never>()
您必须在 Class 文件中导入 Combine 框架才能执行此操作。
这是我正在试验的工作应用程序中的一些代码。
观点:
import SwiftUI
struct ContentView: View {
//Bind using @ObservedObject
@ObservedObject var provider: Provider
var body: some View {
Text("\(self.provider.distance)")
}
...
}
Class:
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
}
}
我不认为 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)
}
}
}
您可以通过点击按钮和开关看到所有内容都处于活动状态并且响应视图中的绑定。