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

Posted

技术标签:

【中文标题】SwiftUI 中的计算(NSObject)属性不会更新视图【英文标题】:Computed (NSObject) Properties in SwiftUI don't update the view 【发布时间】:2019-12-08 21:31:04 【问题描述】:

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

我真正遇到的问题是,当在我的商店中调用 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(),但我的视图由于某种原因没有更新......

【问题讨论】:

您是否考虑了 Beta 5 的更改? sarunw.com/posts/swiftui-changes-in-xcode-11-beta-5 是的,我删除了所有不推荐使用的方法并将所有内容更改为 ObservableObjects,正如您在我的代码中看到的那样。但我没有发现任何关于计算属性的变化或视图如何更新的任何信息。 .. 确认我遇到了同样的问题。 我没有看到你对objectWillChange的实现。 当我有一个符合@ObservableObject 的类的子类时,我看到了类似的问题。 see here 【参考方案1】:

我不认为 N​​SObject 是问题的根源。问题似乎是你没有实现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)
        
    

您可以通过点击按钮和开关来查看所有内容都处于活动状态并响应视图中的绑定。

【讨论】:

但也许我误解了这个问题?是关于你的hasPlans 吗?所以你希望它神奇地重新计算自己?在这种情况下,是的,您需要自己的发布者和自己的订阅;只有在 body 内,绑定更改时才会自动重新计算。【参考方案2】:

用我自己的代码进行实验,出现类似的问题。

当采用 ObservableObject 的 Class 是 NSObject 的子类时,看起来就像 @Publisher SwiftUI 魔法一样。

简短的回答解决方案是,如果您可以在 @Published 不是 NSObject 的子类时使其与它一起工作,那么当您使其成为 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)")
    
    ...

班级:

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
    

【讨论】:

calculateDistance() 是如何调用的?【参考方案3】:

一个可行的解决方案是简单地创建一个 new @State var 而不是使用计算属性。然而,根据 WWDC 关于 SwiftUI 的讨论,这感觉有点不对劲,因为我的“实际状态”存在于我的数据模型中,并且通过声明一个新的 @State 我需要让它们保持同步,这违反了“单一真理来源”模式不是吗?

【讨论】:

以上是关于SwiftUI 中的计算(NSObject)属性不会更新视图的主要内容,如果未能解决你的问题,请参考以下文章

iOS 16 中 CoreData 托管对象发生变化但其衍生 (Derived) 属性在 SwiftUI 中不刷新的解决

iOS 16 中 CoreData 托管对象发生变化但其衍生 (Derived) 属性在 SwiftUI 中不刷新的解决

为 Swiftui 视图转换 @State 的计算属性

如何删除 macOS SwiftUI App 中的“新窗口”选项?

SwiftUI 中子结构的计算属性未更新

SwiftUI 基于计算属性显示警报