iOS15下NavigationView切后台恢复后丢失导航状态 原iOS14.7运行正常
问题原因
- 视图结构不符合规范:当前代码将
TabView嵌套在全局NavigationView内部,苹果官方规范要求每个Tab应持有独立的导航容器,全局共用一个NavigationView会导致各Tab的导航栈状态在视图重绘时互相干扰。 - iOS 15+系统变更:iOS15对NavigationView的生命周期逻辑做了调整,应用切后台返回前台触发父视图重绘时,Stack风格的NavigationView会默认重置导航栈状态。
- 导航状态存储位置不合理:各层级的导航selection状态用
@State存储在子视图内部,当父视图因@ObservedObject(notificationCenter)或@StateObject(localNotification)的属性变更触发整树重绘时,子视图的@State可能因视图标识变更丢失,导致导航栈回退。
解决方案
方案1:调整视图结构(推荐)
将NavigationView下放到每个Tab内部,让每个Tab持有独立的导航栈,从根本上解决状态干扰问题,修改后核心代码如下:
struct MainView: View { var body: some View { TabView { // 每个Tab内部持有自己的NavigationView NavigationView { Tab1() } .navigationViewStyle(.stack) .tabItem { Text("Tab1") } NavigationView { Tab2() } .navigationViewStyle(.stack) .tabItem { Text("Tab2") } } } } // 外层LocalNotificationDemoView去掉包裹的NavigationView struct LocalNotificationDemoView: View { @StateObject var localNotification = LocalNotification() @ObservedObject var notificationCenter: NotificationCenter var body: some View { MainView() } }
方案2:使用SceneStorage持久化导航状态
如果不能调整现有结构,可以将各层级的导航selection状态提升到父视图,用@SceneStorage存储,SceneStorage是系统提供的场景级状态存储能力,切后台、系统回收视图都不会丢失状态:
// 示例:Tab1的selection状态调整为SceneStorage存储 struct Tab1: View { @SceneStorage("tab1Selection") var selection: Int? = nil var body: some View { NavigationLink(destination: View1(), tag: 1, selection: $selection) { Text("Link 1") } .isDetailLink(false) // 额外增加这个修饰符,避免iOS15异常pop } }
同理修改View1里的selection为@SceneStorage存储即可生效。
临时兼容方案
如果上述调整成本较高,可以给全局NavigationView增加固定id,强制视图不被系统重建:
NavigationView { VStack { MainView() } } .navigationViewStyle(.stack) .id("fixedNavigationId") // 固定id避免视图重绘时被重建
内容的提问来源于stack exchange,提问作者Andy
相关产品推荐
相关产品推荐

