iOS 16中NavigationStack返回时导航标题无法自动恢复大标题的问题
在iOS 16中使用NavigationStack内的NavigationLink时,遇到异常行为:从大标题导航视图跳转到设置了.navigationBarTitleDisplayMode(.inline)的inline标题视图后,返回原视图时标题显示模式无法自动恢复为大标题,必须手动下拉页面才能恢复原状态。
预期的默认行为应与苹果官方应用一致:跳转时大标题平滑动画过渡到目标视图的返回按钮,返回时再通过动画恢复为大标题。
经排查,问题源于目标视图的.navigationBarTitleDisplayMode(.inline)修饰符,注释该修饰符可解决问题。此问题在Xcode 14/14.1实时预览中不存在,但在iOS 16/16.1模拟器、真机及iPadOS 16.1的Swift Playgrounds 4.2中均存在。
复现问题的基础代码:
struct Test: View { var body: some View { NavigationStack { List { NavigationLink("Link") { Text("Test") .navigationTitle("Title") .navigationBarTitleDisplayMode(.inline) } } .navigationTitle("Test View") } } }
想了解此问题仅为模拟器bug,还是会出现在生产应用中?由于NavigationView已被弃用,希望尽量避免回退到该组件,以保证代码的前瞻性。
回答
这个问题并非仅模拟器存在的bug,在iOS 16/16.1的真机生产环境中同样会出现,属于NavigationStack在iOS 16早期版本中的已知兼容性问题。
无需回退到已弃用的NavigationView,可以通过以下两种方案解决:
方案1:通过导航路径动态控制标题显示模式
利用NavigationStack的路径绑定,根据当前导航层级动态切换根视图的标题显示模式,返回时路径清空,自动恢复大标题:
struct Test: View { @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { List { Button("Link") { path.append("detail") } } .navigationTitle("Test View") .navigationBarTitleDisplayMode(path.isEmpty ? .large : .inline) .navigationDestination(for: String.self) { _ in Text("Test") .navigationTitle("Title") } } } }
方案2:在目标视图消失时手动触发标题刷新
通过onDisappear回调切换根视图的状态值,触发视图刷新以恢复大标题显示模式:
struct Test: View { @State private var triggerRefresh = true var body: some View { NavigationStack { List { NavigationLink("Link") { Text("Test") .navigationTitle("Title") .navigationBarTitleDisplayMode(.inline) .onDisappear { DispatchQueue.main.async { triggerRefresh.toggle() } } } } .navigationTitle("Test View") .navigationBarTitleDisplayMode(triggerRefresh ? .large : .large) } } }
另外需要注意:苹果在iOS 16.2及后续版本中已经修复了这类NavigationStack的标题过渡问题,建议优先引导用户升级系统,同时在代码中采用上述兼容方案覆盖旧版本用户。
内容的提问来源于stack exchange,提问作者jus10

