从不更新 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)
}
}
我有一个 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)
}
}