Xcode 14.0 beta 2中SwiftUI导航栏及按钮缺失问题咨询
SwiftUI Xcode14 导航栏与工具栏按钮消失问题
问题背景
我本打算就该问题向苹果提交反馈报告,但先在此发帖咨询,确认是否是我遗漏了相关配置,或是最新版SwiftUI有新的开发指引。
以下代码在Xcode 13中运行效果符合预期,但在Xcode 14 beta 2中,导航栏与“Cancel”按钮均消失。采用ProgressView实现内容延迟加载的方案,确认是否是该实现方式在新版本中不再兼容?
问题复现代码
import SwiftUI struct ContentView: View { @State private var isFlowDetermined = false var body: some View { NavigationView { //NestedView() if self.isFlowDetermined { NestedView() } else { ProgressView() .task { await self.determineFlow() } } } } private func determineFlow() async { self.isFlowDetermined = true } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } struct NestedView: View { var body: some View { ScrollView { Text("Where is the \"Cancel\" button?") } .frame(maxWidth: .infinity, maxHeight: .infinity) .background(Color.green) #if !os(macOS) .navigationBarTitleDisplayMode(.inline) #endif .toolbar { #if !os(macOS) ToolbarItem(placement: .navigationBarLeading) { Button("Cancel") { print("got here") } } #endif } } }
异常表现
运行效果如下图所示,导航栏区域既没有显示导航栏,也未展示“Cancel”按钮:
问题原因
这是Xcode14 搭载的iOS16 SDK中NavigationView的行为变更导致的:
- 当
NavigationView的直接子视图为条件渲染的不同类型视图时,系统会在初始加载时根据当前显示的子视图配置导航栏状态。 - 上述示例代码初始状态显示的是
ProgressView,该视图没有添加任何导航栏、工具栏相关修饰符,系统判定当前页面不需要显示导航栏,后续条件切换到NestedView时,没有触发导航栏状态的重新刷新,最终导致导航栏和按钮不显示。 - Xcode13对应的iOS15及更早版本SDK没有该行为逻辑,所以运行正常。
解决方案
方案1(推荐):不切换NavigationView根视图,将加载状态作为覆盖层
保证NavigationView的直接子视图始终是带导航栏配置的NestedView,加载中的ProgressView作为覆盖层展示,从根源避免根视图类型切换导致的导航栏状态异常,修改后的ContentView代码如下:
struct ContentView: View { @State private var isFlowDetermined = false var body: some View { NavigationView { ZStack { // 始终加载带导航栏配置的业务视图 NestedView() // 加载中状态作为覆盖层展示 if !self.isFlowDetermined { ProgressView() .task { await self.determineFlow() } } } } } private func determineFlow() async { self.isFlowDetermined = true } }
方案2:给所有条件分支的子视图添加一致的导航栏配置
如果必须保留根视图条件切换的写法,需要给初始显示的ProgressView也加上和NestedView一致的导航栏配置,保证初始加载时导航栏就被正确初始化:
NavigationView { if self.isFlowDetermined { NestedView() } else { ProgressView() .task { await self.determineFlow() } // 补上和NestedView一致的导航栏配置 #if !os(macOS) .navigationBarTitleDisplayMode(.inline) #endif .toolbar { #if !os(macOS) ToolbarItem(placement: .navigationBarLeading) { // 加载中隐藏按钮做占位 Button("Cancel") {}.hidden() } #endif } } }
补充说明
iOS16开始系统已经将NavigationView标记为废弃,推荐使用NavigationStack实现导航逻辑,新组件对视图切换时的导航栏状态刷新逻辑更稳定。注意:NavigationStack仅支持iOS16及以上系统版本,如果项目需要兼容iOS15及更早版本,优先使用方案1解决问题。
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

