SwiftUI:@State数组在孙视图更新时无法刷新父视图
SwiftUI视图更新不一致问题解决
问题原因
你的代码中Item是值类型(struct),通过@Binding修改其属性时,虽然数组内的元素值已变化,但由于Swift数组的**写时复制(COW)**机制,ItemsMainView的@State var items底层存储未被标记为“已更改”,SwiftUI判定视图状态无变化,因此不会重新渲染ItemsMainView,导致UI显示不一致。
解决方案
方案一:将Item改为ObservableObject类(推荐)
把Item从struct改为遵循ObservableObject的class,用@Published修饰需要监听的属性,修改属性时会自动发送更新通知,触发所有关联视图刷新。
修改后的Item代码:
import Combine public class Item: ObservableObject, Identifiable { public let id: String @Published public var name: String? @Published public var inStock: Bool public init(id: String, name: String?, inStock: Bool) { self.id = id self.name = name self.inStock = inStock } }
修改ItemView,使用@ObservedObject监听Item实例:
struct ItemView: View { @ObservedObject var item: Item @State var draftItemName: String = "" var body: some View { Text("ItemView has") HStack { TextField("TextField", text: $draftItemName) .onSubmit { item.name = draftItemName } Spacer() Text(item.inStock.description) } .onAppear { draftItemName = item.name ?? "" } } }
修改ItemsView的ForEach,直接传递Item实例:
struct ItemsView: View { @Binding var items: [Item] var body: some View { Button("Add new item (call made from ItemsView)", action: { self.items.append(Item(id: UUID().uuidString, name: "Test #1", inStock: false)) }) VStack { ForEach(items) { item in ItemView(item: item) } } } }
方案二:保持Item为struct,手动触发数组更新
如果不想修改Item的类型,可在修改属性后手动替换数组中的元素,生成新的数组实例,让@State检测到变化。
修改ItemView,传入数组的Binding和Item的id,手动更新数组:
struct ItemView: View { @Binding var items: [Item] let itemId: String @State var draftItemName: String = "" private var currentItem: Item { items.first(where: { $0.id == itemId })! } var body: some View { Text("ItemView has") HStack { TextField("TextField", text: $draftItemName) .onSubmit { guard let index = items.firstIndex(where: { $0.id == itemId }) else { return } var updatedItem = items[index] updatedItem.name = draftItemName items[index] = updatedItem } Spacer() Text(currentItem.inStock.description) } .onAppear { draftItemName = currentItem.name ?? "" } } }
修改ItemsView的ForEach,传递数组Binding和Item id:
struct ItemsView: View { @Binding var items: [Item] var body: some View { Button("Add new item (call made from ItemsView)", action: { self.items.append(Item(id: UUID().uuidString, name: "Test #1", inStock: false)) }) VStack { ForEach(items, id: \.id) { item in ItemView(items: $items, itemId: item.id) } } } }
内容的提问来源于stack exchange,提问作者fingia
相关产品推荐
相关产品推荐

