为了简单起见,我修改了我的项目并创建了可重现的代码。
考虑我有以下
Item
数据结构:
struct Item: Identifiable {
let id = UUID()
let isChecked: AnyPublisher<Bool, Never>
let name: String
}
我有以下
ItemCell
,当 isChecked
值发布任何新值时应该更新。
struct ItemCell: View {
var item: Item
@State var imgState: Bool = false
var cancellables = Set<AnyCancellable>()
init(_ item: Item) {
self.item = item
item.isChecked
.print("State for item \(item.name): ") // --> Check this print
.assign(to: \.imgState, on: self)
.store(in: &cancellables)
}
var body: some View {
HStack {
HStack {
Image(systemName: imgState ? "checkmark.circle.fill" : "checkmark.circle")
.foregroundStyle(.blue)
Text(item.name)
}
}
}
}
我已在调试控制台中检查到
isChecked
始终发出值,但视图未更新。
无论如何,要测试我创建的虚拟代码,您只需复制并粘贴即可测试:
完整代码:
import SwiftUI
import Combine
struct Item: Identifiable {
let id = UUID()
let isChecked: AnyPublisher<Bool, Never>
let name: String
}
struct ItemCell: View {
var item: Item
@State var imgState: Bool = false
var cancellables = Set<AnyCancellable>()
init(_ item: Item) {
self.item = item
item.isChecked
.print("State for item \(item.name): ")
.assign(to: \.imgState, on: self)
.store(in: &cancellables)
}
var body: some View {
HStack {
HStack {
Image(systemName: imgState ? "checkmark.circle.fill" : "checkmark.circle")
.foregroundStyle(.blue)
Text(item.name)
}
}
}
}
struct ContentView: View {
var viewModel = ContentViewModel()
var body: some View {
List(viewModel.items) { item in
ItemCell(item)
}
}
}
class ContentViewModel: ObservableObject {
var itemNames = ["A", "B", "C", "D"]
@Published var checkList = [false, false, false, false]
init() {
updateChecklist()
}
// Dummy function to update data
func updateChecklist() {
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] _ in
for index in 0...3 {
self?.checkList[index] = Bool.random()
}
}
}
// Dummy items whose isChecked data are continuously updating with publisher
var items: [Item] {
(0...3).map { index in
Item(
isChecked: $checkList
.map { $0[index] == true }
.eraseToAnyPublisher(),
name: itemNames[index]
)
}
}
}
#Preview {
ContentView()
}
我预计当
isChecked
发布者发出值时,SwiftUI 视图 ItemCell
也应该更新。但还没更新。
一般来说,您应该使用
onReceive
来监听发布者发布的值:
struct ItemCell: View {
var item: Item
@State var imgState: Bool = false
init(_ item: Item) {
self.item = item
}
var body: some View {
HStack {
HStack {
Image(systemName: imgState ? "checkmark.circle.fill" : "checkmark.circle")
.foregroundStyle(.blue)
Text(item.name)
}
}
.onReceive(item.isChecked.print("State for item \(item.name): ")) {
imgState = $0
}
}
}
这对于您的玩具示例来说是正确的。
远离你人为的示例,假设发布的值来自某个外部源,而不是
@Published
属性(这是非常人为的)。您的 @Published
中应该有一个 [Item]
类型的 ContentViewModel
属性。 sink
分配给此数组中的 Item
的发布者。
那么
ItemView
不需要任何代码来观察变化。 ContentView
观察 ContentViewModel.items
,并自动传播更改。
struct Item: Identifiable {
let name: String
var isChecked: Bool
// suppose Item is identified by its name
var id: String { name }
}
struct ItemCell: View {
let item: Item
init(_ item: Item) {
self.item = item
}
var body: some View {
HStack {
HStack {
Image(systemName: item.isChecked ? "checkmark.circle.fill" : "checkmark.circle")
.foregroundStyle(.blue)
Text(item.name)
}
}
}
}
struct ContentView: View {
@StateObject var viewModel = ContentViewModel()
var body: some View {
List(viewModel.items) { item in
ItemCell(item)
}
}
}
class ContentViewModel: ObservableObject {
@Published var items: [Item] = []
private var cancellables: Set<AnyCancellable> = []
init() {
makeItem(name: "Foo")
makeItem(name: "Bar")
makeItem(name: "Baz")
}
func makeItem(name: String) {
items.append(Item(name: name, isChecked: false))
let someExternalPublisher = // get a publisher from somewhere...
// for example let's just say this is a timer publisher
Timer.publish(every: 2, on: .main, in: .default)
.autoconnect()
.map { _ in Bool.random() }
someExternalPublisher
.sink { bool in
if let index = self.items.firstIndex(where: { $0.name == name }) {
self.items[index].isChecked = bool
}
}
.store(in: &cancellables)
}
}
在 iOS 17+ 上,您可以将
Item
设为 @Observable class
,并让每个项目管理自己的发布者:
@Observable
class Item: Identifiable {
let name: String
var isChecked: Bool = false
@ObservationIgnored
var cancellables = Set<AnyCancellable>()
init(name: String) {
self.name = name
let someExternalPublisher = // get a publisher from somewhere...
// for example let's just say this is a timer publisher
Timer.publish(every: 2, on: .main, in: .default)
.autoconnect()
.map { _ in Bool.random() }
someExternalPublisher.sink {
self.isChecked = $0
}
.store(in: &cancellables)
}
}
这对
ObservableObject
不起作用,因为 SwiftUI 无法观察已放入数组的 ObservableObject
的变化。