SwiftUI dismiss所有活跃presented视图并pop到根视图ContentView的问题咨询
现有代码的逻辑是点击ThirdView的返回按钮先dismiss当前弹出的sheet,等sheet完全消失后才会触发SecondView的onDismiss回调执行pop操作,这个过程会短暂显示SecondView,无法达到直接返回FirstView的效果。
解决方案:统一管理路由状态,全局控制导航与弹窗显隐
通过ObservableObject封装所有路由状态,注入到视图环境中,底层视图可以直接修改根视图的导航状态,同时临时禁用动画避免中间过渡效果。
完整修改代码:
import SwiftUI // 统一路由状态管理类 class RouteState: ObservableObject { // 控制是否展示push出来的LoginView(对应原代码SecondView) @Published var showLoginView = false // 控制是否展示present出来的HomeView(对应原代码ThirdView) @Published var showHomeView = false } struct FirstView: View { @StateObject private var routeState = RouteState() var body: some View { NavigationView { VStack(spacing: 16) { Text("FirstView View") .font(.largeTitle) .foregroundColor(.red) NavigationLink( destination: SecondView(), isActive: $routeState.showLoginView, label: { Text("Show SecondView") }) } } .environmentObject(routeState) } } struct SecondView: View { @EnvironmentObject var routeState: RouteState var body: some View { VStack(spacing: 16) { Text("Second View") .font(.largeTitle) .foregroundColor(Color(.systemPurple)) Button(action: { routeState.showHomeView = true }, label: { Text("Show ThirdView") .padding() }) } .sheet(isPresented: $routeState.showHomeView) { ThirdView() .environmentObject(routeState) } } } struct ThirdView: View { @EnvironmentObject var routeState: RouteState var body: some View { VStack(spacing: 16) { Text("Third View") .font(.largeTitle) .foregroundColor(Color(.systemTeal)) Button(action: { // 临时禁用动画,避免中间页过渡效果 UIView.setAnimationsEnabled(false) // 同时关闭弹窗和push导航 routeState.showHomeView = false routeState.showLoginView = false // 延迟恢复动画,不影响后续页面交互 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { UIView.setAnimationsEnabled(true) } }, label: { Text("Back to FirstView") .padding() }) } } }
实现说明
- 所有导航、弹窗的显隐状态统一由RouteState托管,不需要依赖各层视图的presentationMode逐层返回
- 点击返回时直接将根视图的导航状态、弹窗状态同时置为false,配合临时禁用系统动画,不会出现中间页闪屏的问题
- 代码兼容iOS13及以上版本,若使用iOS16+可替换为NavigationStack实现更简洁的路由控制
内容的提问来源于stack exchange,提问作者DRN
相关产品推荐
相关产品推荐

