从不更新 SwiftUI 视图的 @Published 对象数组计算 属性

Computed property from @Published array of objects not updating SwiftUI view

我有一个 StateController class:

import Foundation
import Combine

class StateController: ObservableObject {
    
    // Array of subjects loaded in init() with StorageController
    @Published var subjects: [Subject]
    
    private let storageController = StorageController()
    
    init() {
        self.subjects = storageController.fetchData()
    }
    
    // MARK: - Computed properties
    
    // Array with all tasks from subjects, computed property
    var allTasks: [Task] {
        var all: [Task] = []
        
        for subject in subjects {
            all += subject.tasks
        }
        print("Computed property updated!")
        return all
    }

    var numberofCompletedTasks: Int {
        return subjects.map({[=11=].tasks.map({[=11=].isCompleted == true})}).count
    }
    
    var numberOfHighPriorityTasks: Int {
        return subjects.map({[=11=].tasks.map({[=11=].priority == 1})}).count
    }
    var numberOfMediumPriorityTasks: Int {
        return subjects.map({[=11=].tasks.map({[=11=].priority == 2})}).count
    }
    var numberOfLowPriorityTasks: Int {
        return subjects.map({[=11=].tasks.map({[=11=].priority == 3})}).count
    }
}

还有一个 SwiftUI 视图:

import SwiftUI

struct SmartList: View {
    
    // MARK: - Properties
    let title: String
    
    @EnvironmentObject private var stateController: StateController
    
    // MARK: - View body
    
    var body: some View {
        
        List(stateController.allTasks, id: \.taskID) { task in
            
            TaskView(task: task)
                .environmentObject(self.stateController)
            
        }.listStyle(InsetGroupedListStyle())
        .navigationTitle(LocalizedStringKey(title))
    }
}

当我更新“subjects”@Published 数组中的“Task”对象时,例如检查它们是否完整,SwiftUI 应该自动更新视图,因为计算的属性是从 ObservableObject 的 @Published 属性 派生的(在视图中声明为@EnvironmentObject)但它不起作用。

如何将我的 SwiftUI 视图绑定到从 @Published 属性 派生的计算属性?

遗憾的是,SwiftUI 在 @EnvironmentObject/@ObservedObject 更改时自动更新显示计算属性的视图仅在非常有限的情况下有效。即 @Published 属性 本身不能是引用类型,它需要是值类型(或者如果是引用类型,则需要替换整个引用,只需更新一个 属性 所述引用类型不会触发 objectWillChange 发射,因此不会触发 View 重新加载)。

因此,您不能依赖 SwiftUI 的计算属性。相反,您需要将视图需要的所有属性都存储起来并将它们标记为 @Published。然后,您需要在 @Published 属性 上设置订阅,计算属性需要其值,因此每次存储属性所依赖的值发生变化时都会更新存储属性的值。

class StateController: ObservableObject {

    // Array of subjects loaded in init() with StorageController
    @Published var subjects: [Subject]

    @Published var allTasks: [Task] = []

    private let storageController = StorageController()

    private var subscriptions = Set<AnyCancellable>()

    init() {
        self.subjects = storageController.fetchData()
        // Closure for calculating the value of `allTasks`
        let calculateAllTasks: (([Subject]) -> [Task]) = { subjects in subjects.flatMap { [=10=].tasks } }
        // Subscribe to `$subjects`, so that each time it is updated, pass the new value to `calculateAllTasks` and then assign its output to `allTasks`
        self.$subjects.map(calculateAllTasks).assign(to: \.allTasks, on: self).store(in: &subscriptions)
    }
}