SwiftUI返回父视图时navigationBarTitle无法从.inline恢复为.large
SwiftUI 导航栏标题模式无法自动恢复的问题解决办法
问题描述
在iOS16的模拟器或真机中,使用自定义Cancel按钮(通过@Environment(.dismiss))从子视图返回父视图时,父视图的导航栏标题无法自动从子视图的.inline模式恢复为原本设置的.large模式,只有下拉列表才会触发恢复;部分机型(如iPhone14Pro)还会出现模式切换动画生硬的情况,而Xcode Live Preview中该问题可能不会出现。
复现代码
struct ContentView: View { @State private var sheetIsPresented = false var items = ["Item1", "Item2", "Item3"] var body: some View { NavigationStack { List { ForEach(items, id: \.self) { item in NavigationLink(item, destination: DestinationView(item: item)) .padding() } } .navigationBarTitle("Home", displayMode: .large) .listStyle(.plain) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { sheetIsPresented.toggle() } label: { Image(systemName: "plus") } } } } .sheet(isPresented: $sheetIsPresented) { NavigationStack { DestinationView(item: "New!") } } } } struct DestinationView: View { var item: String @Environment(.dismiss) private var dismiss var body: some View { List { Text(item) } .toolbar { ToolbarItem (placement: .navigationBarLeading) { Button("Cancel") { dismiss() } } } .listStyle(.plain) .navigationBarTitleDisplayMode(.inline) .navigationBarBackButtonHidden() } }
问题原因
这是SwiftUI导航栈状态同步的已知bug:当使用自定义返回按钮而非系统默认的导航返回按钮时,导航栏的标题模式状态没有正确回滚到父视图的配置,导致父视图标题模式异常。
解决方案
以下是几种可行的临时解决办法:
方法1:子视图消失时重置父视图标题模式
在父视图中通过状态变量控制标题模式,子视图消失时触发重置:
struct ContentView: View { @State private var sheetIsPresented = false @State private var titleDisplayMode: NavigationBarItem.TitleDisplayMode = .large var items = ["Item1", "Item2", "Item3"] var body: some View { NavigationStack { List { ForEach(items, id: \.self) { item in NavigationLink(item, destination: DestinationView(item: item) .onDisappear { titleDisplayMode = .large }) .padding() } } .navigationBarTitle("Home", displayMode: titleDisplayMode) .listStyle(.plain) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { sheetIsPresented.toggle() } label: { Image(systemName: "plus") } } } } .sheet(isPresented: $sheetIsPresented) { NavigationStack { DestinationView(item: "New!") .onDisappear { titleDisplayMode = .large } } } } }
方法2:使用NavigationPath手动管理导航栈
通过绑定导航路径,在自定义返回时手动更新栈状态,触发标题模式恢复:
struct ContentView: View { @State private var sheetIsPresented = false @State private var navigationPath = NavigationPath() var items = ["Item1", "Item2", "Item3"] var body: some View { NavigationStack(path: $navigationPath) { List { ForEach(items, id: \.self) { item in NavigationLink(item, value: item) .padding() } } .navigationDestination(for: String.self) { item in DestinationView(item: item, dismissAction: { navigationPath.removeLast() }) } .navigationBarTitle("Home", displayMode: .large) .listStyle(.plain) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { sheetIsPresented.toggle() } label: { Image(systemName: "plus") } } } } .sheet(isPresented: $sheetIsPresented) { NavigationStack { DestinationView(item: "New!", dismissAction: {}) } } } } struct DestinationView: View { var item: String var dismissAction: () -> Void @Environment(.dismiss) private var dismiss var body: some View { List { Text(item) } .toolbar { ToolbarItem (placement: .navigationBarLeading) { Button("Cancel") { dismissAction() dismiss() } } } .listStyle(.plain) .navigationBarTitleDisplayMode(.inline) .navigationBarBackButtonHidden() } }
方法3:全局强制恢复大标题模式(不推荐,影响全局)
通过UIKit的API强制设置导航栏偏好,适合仅单一导航栈的场景:
struct ContentView: View { @State private var sheetIsPresented = false var items = ["Item1", "Item2", "Item3"] var body: some View { NavigationStack { List { ForEach(items, id: \.self) { item in NavigationLink(item, destination: DestinationView(item: item)) .padding() } } .navigationBarTitle("Home", displayMode: .large) .listStyle(.plain) .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { sheetIsPresented.toggle() } label: { Image(systemName: "plus") } } } } .onAppear { UINavigationBar.appearance().prefersLargeTitles = true } .sheet(isPresented: $sheetIsPresented) { NavigationStack { DestinationView(item: "New!") } } } }
内容的提问来源于stack exchange,提问作者Gallaugher
相关产品推荐
相关产品推荐

