SwiftUI中如何在被弹出视图的单个按钮中同时实现关闭当前视图并弹出新视图
SwiftUI中如何在被弹出视图的单个按钮中同时实现关闭当前视图并弹出新视图
嘿,我来帮你搞定这个需求!先理清楚你的场景:你是用UIHostingController把FirstView作为模态视图弹出来,然后FirstView又通过.sheet弹出了SecondView,现在想在SecondView的按钮上点一下,同时完成「关闭当前的SecondView」和「弹出新视图」这两个操作,对吧?
下面分两种常见的需求场景给你具体的实现方案:
场景1:关闭当前Sheet后,在FirstView中弹出新的Sheet
这种情况是指关闭SecondView这个sheet后,在原来的FirstView里弹出另一个新的视图(比如ThirdView)。因为SwiftUI的sheet状态是由父视图控制的,咱们需要把状态和逻辑往上传递:
- 首先在
FirstView里新增一个控制新视图弹出的状态变量,同时把SecondView的关闭状态和新视图的弹出状态绑定传递过去:
struct FirstView: View { @Environment(\.presentationMode) var presentationMode @State private var showSecondView = false @State private var showThirdView = false // 控制新视图的弹出状态 var body: some View { VStack { Button("弹出SecondView") { showSecondView = true } } // 弹出SecondView的sheet .sheet(isPresented: $showSecondView) { SecondView( isSecondViewPresented: $showSecondView, triggerThirdView: $showThirdView ) } // 弹出新视图的sheet .sheet(isPresented: $showThirdView) { ThirdView() // 这里替换成你自己的新视图 } } }
- 然后在
SecondView里,通过绑定控制自己的关闭,同时触发新视图的弹出:
struct SecondView: View { @Binding var isSecondViewPresented: Bool @Binding var triggerThirdView: Bool var body: some View { VStack { Button("关闭当前并弹出新视图") { // 先关闭当前的SecondView isSecondViewPresented = false // 因为sheet关闭是异步操作,咱们延迟一点点再触发新弹出(避免动画冲突) DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { triggerThirdView = true } } } } }
如果你觉得用延迟不够优雅,也可以在FirstView里监听showSecondView的状态变化,当它从true变成false时自动触发新视图的弹出,这样就不用写延迟了:
// 修改FirstView的sheet部分,加上onChange监听 .sheet(isPresented: $showSecondView) { SecondView(isSecondViewPresented: $showSecondView) } .onChange(of: showSecondView) { oldValue, newValue in // 当SecondView被关闭(状态从true变false),就弹出新视图 if oldValue && !newValue { showThirdView = true } }
对应的SecondView就只需要处理自己的关闭逻辑:
struct SecondView: View { @Binding var isSecondViewPresented: Bool var body: some View { VStack { Button("关闭当前并弹出新视图") { isSecondViewPresented = false } } } }
场景2:关闭当前Sheet + 关闭FirstView模态,然后弹出新视图
如果你的需求是:关闭SecondView后,同时把FirstView这个由UIHostingController弹出的模态视图也关掉,然后再弹出新的视图,那咱们需要借助闭包来和UIKit的代码交互:
- 首先在创建
FirstView的时候,传一个闭包进去,用来通知原来的UIKit代码执行“关闭当前模态+弹出新视图”的操作:
// 原来的UIKit代码修改 let firstView = FirstView(onTriggerNewView: { [weak someVC, weak hostingController] in // 先关闭FirstView的模态视图 hostingController?.dismiss(animated: true) { // 关闭完成后,弹出新的视图 let newHostingVC = UIHostingController(rootView: ThirdView()) someVC?.present(newHostingVC, animated: true) } }) let hostingController = UIHostingController(rootView: firstView) someVC.present(hostingController, animated: true)
- 然后
FirstView里把这个闭包传递给SecondView,同时处理SecondView的关闭:
struct FirstView: View { @Environment(\.presentationMode) var presentationMode @State private var showSecondView = false let onTriggerNewView: () -> Void var body: some View { VStack { Button("弹出SecondView") { showSecondView = true } } .sheet(isPresented: $showSecondView) { SecondView(onAction: { // 先关闭SecondView showSecondView = false // 延迟一下触发闭包,避免动画冲突 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { onTriggerNewView() } }) } } }
- 最后
SecondView里的按钮直接调用这个闭包就行:
struct SecondView: View { let onAction: () -> Void var body: some View { VStack { Button("关闭当前并弹出新视图") { onAction() } } } }
两种方案你可以根据自己的实际需求选,核心思路就是把状态或回调往上传递,因为SwiftUI的模态视图状态都是由父级控制的,咱们没法直接在子视图里强制关闭父级的模态,得通过绑定或者闭包来联动~
备注:内容来源于stack exchange,提问作者user6631314
相关产品推荐
相关产品推荐

