SwiftUI问题:关闭Popover Sheet后跳转至意外目标页面
解决Xcode15.3中关闭Sheet后意外跳转到导航栈根页面的问题
问题根源
Xcode15.3对应的iOS17版本中,旧的NavigationView组件行为出现兼容性调整——当sheet挂载在导航栈根视图层级时,关闭sheet可能意外触发导航栈弹出操作。另外,如果sheet的绑定状态变量作用域在导航栈根视图而非当前页面,也会导致状态变化干扰整个导航栈的状态。
具体解决方案
替换
NavigationView为NavigationStack
iOS16+官方推荐使用NavigationStack替代旧的NavigationView,它对导航栈的管理更稳定,兼容性更好,能避免旧组件的行为异常。将Sheet的绑定状态变量移至当前页面(PageB)
不要把sheet的状态变量放在导航栈根视图,确保状态仅作用于打开sheet的页面(PageB),防止状态变化影响整个导航栈。
代码示例
修正前的错误写法(易触发问题)
struct RootView: View { @State private var showPageB = false @State private var popoverOpen1 = false var body: some View { NavigationView { PageA(showPageB: $showPageB) .navigationDestination(isPresented: $showPageB) { PageB(popoverOpen1: $popoverOpen1) } } // Sheet挂载在根视图,关闭时可能干扰导航栈 .sheet(isPresented: $popoverOpen1) { SheetC(popoverOpen1: $popoverOpen1) } } }
修正后的写法
// 使用NavigationStack替代NavigationView struct RootView: View { @State private var navigationPath = NavigationPath() var body: some View { NavigationStack(path: $navigationPath) { PageA(navigationPath: $navigationPath) .navigationDestination(for: PageRoute.self) { route in switch route { case .pageB: PageB() } } } } } // 定义导航路由枚举 enum PageRoute { case pageB } // 页面A struct PageA: View { @Binding var navigationPath: NavigationPath var body: some View { Button("跳转到页面B") { navigationPath.append(PageRoute.pageB) } } } // 页面B:将Sheet的状态变量放在本地 struct PageB: View { @State private var popoverOpen1 = false var body: some View { Button("打开Popover Sheet C") { popoverOpen1 = true } // Sheet挂载在PageB层级,状态仅作用于当前页面 .sheet(isPresented: $popoverOpen1) { SheetC(popoverOpen1: $popoverOpen1) } } } // Sheet C struct SheetC: View { @Binding var popoverOpen1: Bool var body: some View { Button("取消") { popoverOpen1 = false } } }
补充说明
如果需要兼容iOS15及以下必须保留NavigationView,可以尝试为sheet设置合适的.presentationDetents,同时确保sheet的状态变量是PageB的本地状态,避免根视图状态变化影响导航栈。但优先推荐使用NavigationStack,它是苹果官方主推的导航组件,后续会持续优化维护。
内容的提问来源于stack exchange,提问作者Dang Vu
相关产品推荐
相关产品推荐

