You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 07:55:18