SwiftUI使用@Environment(\.dismiss)结合NavigationView/NavigationStack时崩溃
SwiftUI Sheet切换崩溃原因及解决办法
崩溃原因
核心问题出在复用同一个.sheet(item:)绑定变量切换Sheet时的上下文冲突:
当你点击Sheet一中的按钮,直接把bottomSheetType从.one改成.two,SwiftUI会尝试在当前Sheet还没完全关闭的情况下,立刻弹出新的Sheet。而视图B中存在的@Environment(\.dismiss)会关联当前Sheet的Presentation上下文,加上NavigationView/NavigationStack的层级约束,系统检测到同一个父控制器正在present一个视图控制器,又要present另一个,直接触发层级冲突崩溃。
解决办法
办法1:先关闭当前Sheet,再打开新Sheet
手动调用dismiss()释放当前上下文,再异步触发新Sheet的弹出,确保操作顺序正确:
case .one: Button("This is sheet one, click to open sheet two") { dismiss() DispatchQueue.main.async { self.bottomSheetType = .two } }
办法2:用独立绑定实现嵌套Sheet
为每个Sheet单独设置状态变量,让每个Sheet的弹出上下文完全独立,彻底避免冲突:
struct B: View { @Environment(\.dismiss) var dismiss @State private var showSheetOne = false @State private var showSheetTwo = false var body: some View { VStack(spacing: 0) { Button(action: { showSheetOne = true }, label: { Text("Click to open sheet A") }) } .sheet(isPresented: $showSheetOne) { Button("This is sheet one, click to open sheet two") { showSheetTwo = true } .sheet(isPresented: $showSheetTwo) { Text("This is sheet two") } } } }
办法3:复用枚举但通过回调确保顺序
如果要保留BottomSheetType枚举的用法,可以借助临时变量和异步操作,确保当前Sheet关闭后再打开新的:
.sheet(item: $bottomSheetType) { bottomSheetType in switch bottomSheetType{ case .one: Button("This is sheet one, click to open sheet two") { let targetSheet = BottomSheetType.two dismiss() DispatchQueue.main.async { self.bottomSheetType = targetSheet } } case .two: Text("This is sheet two") } }
内容的提问来源于stack exchange,提问作者Karan Peshwani
相关产品推荐
相关产品推荐

