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

SwiftUI中@State更新后自定义CustomView未同步刷新问题

SwiftUI自定义容器视图嵌套时@State数组更新不刷新问题

问题现象

  • 基于Swift + SwiftUI开发应用时,数组类型的@State属性定义在父视图层级,更新数组元素可正常触发视图响应刷新
  • 完全相同的属性与逻辑代码放入自定义容器CustomView中时,状态更新无法触发UI同步刷新
  • 出现问题的CustomView是仅做布局承载的简单容器视图,逻辑与父视图中可正常运行的代码无差异,仅在嵌套到自定义内部视图时,值更新后UI不会同步变化

问题效果截图

复现代码

struct MyItem: Identifiable, Hashable {
    var text: String
    var id = UUID()
}

struct TempView: View {

    @State var outerItems = [MyItem]()
    @State var innerItems = [MyItem]()

    var body: some View {
        VStack() {
            ForEach(outerItems) { entry in
                VStack {
                    Text(entry.text)
                }
            }
            Button(action: {
                self.outerItems.append(MyItem(text: "Inner Item:\(outerItems.count)"))
            }) {
                Text("Add Outer Item")
            }

            Divider()

            CustomView {
                VStack {
                    ForEach(innerItems) { entry in
                        VStack {
                            Text(entry.text)
                        }
                    }
                    Button(action: {
                        self.innerItems.append(MyItem(text: "Outer Item:\(innerItems.count)"))
                    }) {
                        Text("Add inner Item")
                    }
                }
            }
        }
    }
}

struct CustomView<Content: View>: View {
    // 错误写法:给传入的内容闭包加了@State标记
    @State var content: () -> Content
    var body: some View {
        content()
    }
}

问题根因

错误点在自定义CustomView的content属性定义:@State是SwiftUI提供给当前视图管理自有、内部状态的属性包装器,被@State标记的属性会被SwiftUI在视图生命周期内缓存持有,仅当属性本身的值变更时才会触发当前视图body重新计算。
而传入的content闭包是父视图构建的视图内容,不属于CustomView自身的内部状态,给它加@State后,SwiftUI会在CustomView首次加载时缓存这个闭包,后续父视图的innerItems状态变更时,CustomView不会重新获取更新后的内容闭包,一直使用首次缓存的旧闭包渲染,自然不会触发UI刷新。

修复方案

删除content属性上错误的@State标记,按照SwiftUI自定义容器的标准写法,用@ViewBuilder标记内容闭包即可:

struct CustomView<Content: View>: View {
    private let content: () -> Content
    
    init(@ViewBuilder content: @escaping () -> Content) {
        self.content = content
    }
    
    var body: some View {
        content()
    }
}

修改后外部传入的内容闭包会跟随父视图的状态变化自动重新构建,点击内部添加按钮即可正常触发列表刷新。

注意:@State、@StateObject这类状态包装器仅用于修饰视图自身所有、不依赖外部传入的状态值,外部传入的视图构建闭包不要使用状态包装器修饰。

内容的提问来源于stack exchange,提问作者user8719510

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:15:32