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

时间:2020-09-09 09:54:54

标签: swift swiftui computed-properties

我有一个StateController类:

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({$0.tasks.map({$0.isCompleted == true})}).count
    }
    
    var numberOfHighPriorityTasks: Int {
        return subjects.map({$0.tasks.map({$0.priority == 1})}).count
    }
    var numberOfMediumPriorityTasks: Int {
        return subjects.map({$0.tasks.map({$0.priority == 2})}).count
    }
    var numberOfLowPriorityTasks: Int {
        return subjects.map({$0.tasks.map({$0.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))
    }
}

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

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

1 个答案:

答案 0 :(得分:0)

可悲的是,当@EnvironmentObject / @ObservedObject更改时,SwiftUI自动更新显示计算属性的视图仅在非常有限的情况下有效。也就是说,@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 { $0.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)
    }
}