SwiftUI中ForEach循环内的按钮如何绑定独立Popover弹窗
解决方案
首先明确问题根源:所有按钮共用同一个布尔类型的presentPopover状态,SwiftUI无法匹配该状态对应的触发按钮,导致弹窗无法正常弹出。
推荐两种可行实现方式:
方案1:使用popover(item:) 重载(更推荐)
该方案是SwiftUI针对多元素触发弹窗的原生适配方案,无需手动管理每个元素的显隐状态。
- 替换原有状态变量:
// 替换原来的 @State private var presentPopover = false @State private var selectedIndex: Int?
- 给Int添加Identifiable适配(用于
popover(item:)的参数要求):
extension Int: Identifiable { public var id: Self { self } }
- 修改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一致:
@State private var selectedIndex: Int?
- 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
相关产品推荐
相关产品推荐

