我有一个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属性派生的计算属性?
答案 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)
}
}