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

SwiftUI .actionSheet内部变量无法更新 点击不同条目显示初始数据

问题根因

这是SwiftUI中基于isPresented控制的弹窗的常见问题,由两个原因共同导致:

  • 所有列表条目共用同一个布尔类型的弹窗控制状态,系统无法区分当前触发弹窗对应的具体条目
  • isPresented版本的弹窗内容闭包会在视图首次渲染时完成变量捕获,后续状态变更不会更新捕获的变量值,因此永远展示第一次触发弹窗时的条目数据

示例截图

修复方案

方案1:使用item版本的弹窗(推荐)

该方案通过可选类型的选中条目状态控制弹窗,系统会自动在选中条目更新时重新渲染弹窗内容,永远匹配最新选中的数据。

步骤1:新增状态变量

// 替换原有的@State var showActionSheet = false
@State private var selectedRun: RunSourceSample?

步骤2:修改父视图布局代码

var body: some View {
    VStack {
        ForEach(sampleSourceData) { link in
            VStack {
                SampleView(day: link.day, distance: link.distance, exercise: link.exercise) {
                    // 点击按钮时存储当前选中的条目
                    selectedRun = link
                }
            }
        }
    }
    // 将actionSheet移到列表外层,绑定到选中条目
    .actionSheet(item: $selectedRun) { run in
        ActionSheet(title: Text("\(run.day): \(run.distance) \(run.exercise)"),
                    message: Text("Stay Golden"),
                    buttons: [
                        .cancel(),
                        .default(
                            Text("Action 1")
                        ),
                        .default(
                            Text("Action 2")
                        )
                    ]
        )
    }
}

步骤3:修改子视图的点击回调

struct SampleView: View {
    var day: String
    var distance: String
    var exercise: String
    // 替换原有的Binding,用闭包传递点击事件
    var onTap: () -> Void

    var body: some View {
        HStack {
            Text(day)
            Text(distance)
            Text(exercise)
            Spacer()
            Button(action: {
                onTap()
            }) {
                Image(systemName: "ellipsis")
            }
        }
        .padding()
        .background(Color("BlueGray"))
        .cornerRadius(13)
    }
}

方案2:将弹窗下放到子视图内部

每个子视图独立管理自己的弹窗状态,避免共用状态导致的数据错乱,修改成本更低。
直接修改SampleView代码即可,父视图不需要调整:

struct SampleView: View {
    var day: String
    var distance: String
    var exercise: String
    // 子视图自己持有弹窗状态,不需要外部传入Binding
    @State private var showActionSheet = false

    var body: some View {
        HStack {
            Text(day)
            Text(distance)
            Text(exercise)
            Spacer()
            Button(action: {
                showActionSheet = true
            }) {
                Image(systemName: "ellipsis")
            }
        }
        .padding()
        .background(Color("BlueGray"))
        .cornerRadius(13)
        // 弹窗移到子视图内部,直接使用当前子视图的属性即可拿到正确数据
        .actionSheet(isPresented: $showActionSheet) {
            ActionSheet(title: Text("\(day): \(distance) \(exercise)"),
                        message: Text("Stay Golden"),
                        buttons: [
                            .cancel(),
                            .default(
                                Text("Action 1")
                            ),
                            .default(
                                Text("Action 2")
                            )
                        ]
            )
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 03:24:03