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

SwiftUI列表点击不同单元格按钮时Popover无法正常关闭如何解决

问题原因

你当前的实现为每个列表单元格单独定义了Bool类型的showingPopover状态,点击不同单元格时会有多个状态同时为true。SwiftUI的弹出层规则同一时间仅允许展示一个popover,系统检测到已有弹出层在展示时就会抛出你遇到的already presenting报错,旧弹出层不会自动关闭,新弹出层也无法正常渲染。

推荐解决方案

使用全局唯一的可选类型状态记录当前激活的单元格ID,替代每个单元格单独维护的Bool状态,保证同一时间最多只有一个弹出层处于激活状态。

  1. 首先为你的列表数据模型遵守Identifiable协议,确保每个单元格有唯一ID:
struct ListItem: Identifiable {
    let id = UUID()
    // 其他业务属性
}
  1. 替换原有的单单元格Bool状态,改为全局存储激活ID:
// 原代码每个单元格的 @State private var showingPopover = false 废弃
@State private var activePopoverItemID: UUID? = nil
  1. 调整按钮点击逻辑和popover绑定规则:
Button(action: {
    // 点击直接赋值当前单元格ID,旧ID会自动失效
    activePopoverItemID = item.id
}, label:  { Image(systemName: "gearshape") })
    .buttonStyle(BorderlessButtonStyle())
    // 自定义绑定:当前单元格ID等于激活ID时展示popover
    .popover(isPresented: .init(
        get: { activePopoverItemID == item.id },
        set: { newValue in
            if !newValue {
                activePopoverItemID = nil
            }
        }
    ), arrowEdge: .leading) {
        AccessoryIconView(viewModel: viewModel.iconViewModel())
            .frame(width: 200, height: 300)
    }

这种实现方式会在你点击新单元格按钮时,自动让旧弹出层的展示条件变为false触发关闭,再弹出新的弹出层,完全符合系统弹出规则,不会产生报错。

临时兼容方案(不推荐)

如果你暂时不想调整状态结构,可以在按钮点击时先清空所有弹出层状态,延迟极短时间再激活当前按钮的弹出层:

Button(action: {
    // 先重置所有单元格的showingPopover为false
    showingPopover = false
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
        showingPopover = true
    }
}, label:  { Image(systemName: "gearshape") })
// 其余popover代码保持不变

该方案依赖硬编码的延迟时间,极端情况下可能出现时序问题,仅作为临时修复使用。

内容的提问来源于stack exchange,提问作者AirXygène

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 07:36:03