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

SwiftUI中ForEach循环内的按钮如何绑定独立Popover弹窗

解决方案

首先明确问题根源:所有按钮共用同一个布尔类型的presentPopover状态,SwiftUI无法匹配该状态对应的触发按钮,导致弹窗无法正常弹出。

推荐两种可行实现方式:

方案1:使用popover(item:) 重载(更推荐)

该方案是SwiftUI针对多元素触发弹窗的原生适配方案,无需手动管理每个元素的显隐状态。

  1. 替换原有状态变量:
// 替换原来的 @State private var presentPopover = false
@State private var selectedIndex: Int?
  1. 给Int添加Identifiable适配(用于popover(item:)的参数要求):
extension Int: Identifiable {
    public var id: Self { self }
}
  1. 修改ForEach内的代码:
ForEach(0..<30, id: \.self) { index in
    Button {
        // 点击时记录当前选中的index
        selectedIndex = index
    } label: {
        ZStack {
            Rectangle()
                .fill(.white)
                .frame(width: 180, height: 55)
                .cornerRadius(5)
            Text("Runde \(index + 1)")
                .bold()
                .font(.system(size: 24))
                .foregroundColor(.black)
        }
    }
    // 替换原有isPresented版本的popover
    .popover(item: $selectedIndex) { currentIndex in
        GameSheetPopOverView(points: $points)
        // 若弹窗需要用到当前按钮的索引,直接使用传入的currentIndex即可
    }
}

方案2:使用自定义Binding(无需扩展系统类型)

如果不想给Int添加全局扩展,可以通过Binding的构造器给每个按钮生成独立的显隐绑定:

  1. 状态变量和方案1一致:
@State private var selectedIndex: Int?
  1. ForEach内popover的绑定改为自定义构造:
.popover(isPresented: Binding(
    get: { selectedIndex == index },
    set: { isPresented in
        if isPresented {
            selectedIndex = index
        } else {
            selectedIndex = nil
        }
    }
)) {
    GameSheetPopOverView(points: $points)
}

可选适配:iPhone端显示气泡样式

默认iPhone端popover会以全屏sheet形式弹出,如果需要保持气泡样式,给弹窗内容添加如下修饰符即可:

GameSheetPopOverView(points: $points)
    .presentationCompactAdaptation(.popover)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 13:06:10