SwiftUI视图切换异常:重复跳转致视图堆叠下移
问题分析与解决方案
核心问题
你当前的按钮实现逻辑完全错误:Button的action闭包里直接创建destinationView实例,但这并不会触发导航跳转,而是相当于在当前视图层级里叠加新视图。当点击指向LoggedMainView的按钮时,新的LoggedMainView会包含BottomMenu,再次点击又会叠加一层,无限嵌套导致视图不断下移。
修复步骤
1. 统一导航状态管理
用NavigationPath和ObservableObject管理导航,避免重复跳转和嵌套:
首先定义页面枚举,方便识别不同页面:
enum AppPage: Hashable { case loggedMain case search case profile }
更新MainViewModel,添加导航路径属性:
class MainViewModel: ObservableObject { @Published var navigationPath = NavigationPath() // 保留你原有的业务逻辑代码 }
2. 重构按钮组件
把BtnMenu改成基于NavigationLink的导航按钮,同时添加重复跳转判断:
struct BtnMenu: View { @ObservedObject var logManager: MainViewModel var btnText: String var btnIcon: String var targetPage: AppPage var body: some View { NavigationLink(value: targetPage) { Text("\(Image(systemName: btnIcon)) \(btnText)") } .onTapGesture { switch targetPage { case .loggedMain: // 点击首页按钮时直接回到根视图,避免嵌套 logManager.navigationPath.removeAll() default: // 非首页按钮,检查是否已经在目标页面,避免重复跳转 guard let lastPage = logManager.navigationPath.last as? AppPage, lastPage != targetPage else { return } logManager.navigationPath.append(targetPage) } } } }
3. 修改底部菜单
传递共享的MainViewModel实例,避免每个组件新建独立实例:
struct BottomMenu: View { @ObservedObject var logManager: MainViewModel var body: some View { HStack { Spacer() BtnMenu(logManager: logManager, btnText: "Dodaj", btnIcon: "plus.app.fill", targetPage: .loggedMain) Spacer() BtnMenu(logManager: logManager, btnText: "Szukaj", btnIcon: "magnifyingglass", targetPage: .search) Spacer() BtnMenu(logManager: logManager, btnText: "Profil", btnIcon: "person.crop.circle.fill", targetPage: .profile) Spacer() } .padding(.bottom, 30) .font(.system(size: 20)) } }
4. 更新根视图配置
在LoggedMainView中使用NavigationStack关联导航路径,并将底部菜单放在工具栏:
struct LoggedMainView: View { @StateObject private var logManager = MainViewModel() var body: some View { NavigationStack(path: $logManager.navigationPath) { // 这里放你的首页内容 Text("首页内容") .navigationDestination(for: AppPage.self) { page in switch page { case .search: SearchView() .toolbar { ToolbarItem(placement: .bottomBar) { BottomMenu(logManager: logManager) } } case .profile: ProfileView() .toolbar { ToolbarItem(placement: .bottomBar) { BottomMenu(logManager: logManager) } } case .loggedMain: // 这里不会触发,因为点击首页按钮直接清空路径回到根 EmptyView() } } .toolbar { ToolbarItem(placement: .bottomBar) { BottomMenu(logManager: logManager) } } } } }
关键说明
- 所有页面共享同一个
MainViewModel实例,保证导航状态一致 - 点击首页按钮时直接清空导航路径回到根视图,彻底避免嵌套
- 非首页按钮会检查当前页面,防止重复跳转相同视图
内容的提问来源于stack exchange,提问作者Kromczetto
相关产品推荐
相关产品推荐

