SwiftUI中toolbarBackground修改器致UINavigationBar.appearance标题属性变更
问题描述
我通过以下代码全局设置导航栏字体样式:
init() { // Initialise Firebase //FirebaseApp.configure() // this is not the same as manipulating the proxy directly let appearance = UINavigationBarAppearance() // this overrides everything you have set up earlier. appearance.configureWithTransparentBackground() // this only applies to big titles appearance.largeTitleTextAttributes = [ .font : UIFont(name: "Poppins", size: 38)!, NSAttributedString.Key.foregroundColor : UIColor(named: "Dark")! ] // this only applies to small titles appearance.titleTextAttributes = [ .font : UIFont(name: "Poppins", size: 20)!, NSAttributedString.Key.foregroundColor : UIColor(named: "Dark")! ] let back = UIBarButtonItemAppearance() back.normal.backgroundImage = UIImage(named: "arrow.left") back.normal.titleTextAttributes = [ .font : UIFont(name: "Poppins", size: 20)!, NSAttributedString.Key.foregroundColor : UIColor(named: "Dark")! ] appearance.backButtonAppearance = back appearance.shadowColor = .clear #if !targetEnvironment(macCatalyst) appearance.backgroundColor = UIColor(named: "White") #endif //In the following two lines you make sure that you apply the style for good UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().standardAppearance = appearance // This property is not present on the UINavigationBarAppearance // object for some reason and you have to leave it til the end UINavigationBar.appearance().tintColor = UIColor(named: "Black") UINavigationBar.appearance().prefersLargeTitles = true UITextView.appearance().backgroundColor = .clear UINavigationBar.appearance().isTranslucent = false UIToolbar.appearance().backgroundColor = UIColor(named: "White") UIToolbar.appearance().isTranslucent = false }
由于SwiftUI的Map会覆盖TabBar和NavigationBar的toolbar外观,我给Map视图添加了以下修改器:
.toolbarBackground(.hidden, for: .tabBar) .toolbarBackground(.hidden, for: .navigationBar)
但这会导致UINavigationBar.appearance()的标题属性被重置为iOS默认样式。我尝试用SwiftUI-Introspect修改导航栏,但未生效,字体仍变回默认:
.introspect( .navigationView(style: .stack), on: .iOS(.v13, .v14, .v15, .v16, .v17), .tvOS(.v13, .v14, .v15, .v16, .v17) ) { navigationController in navigationController.navigationBar.backgroundColor = UIColor.white navigationController.navigationBar.isTranslucent = false let navbarAppearance = UINavigationBarAppearance() navbarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white] navbarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white] navbarAppearance.backgroundColor = UIColor.white navbarAppearance.largeTitleTextAttributes = [ .font : UIFont(name: "Poppins", size: 38)!, NSAttributedString.Key.foregroundColor : UIColor(named: "Dark")! ] // this only applies to small titles navbarAppearance.titleTextAttributes = [ .font : UIFont(name: "Poppins", size: 20)!, NSAttributedString.Key.foregroundColor : UIColor(named: "Dark")! ] navbarAppearance.shadowColor = .clear navbarAppearance.shadowImage = UIImage() UINavigationBar.appearance().standardAppearance = navbarAppearance UINavigationBar.appearance().compactAppearance = navbarAppearance UINavigationBar.appearance().scrollEdgeAppearance = navbarAppearance }
原因分析
.toolbarBackground(.hidden)的重置作用:该修改器会直接覆盖导航栏的全局外观配置,包括之前通过UINavigationBar.appearance()设置的字体属性,强制恢复系统默认样式。- Introspect目标不匹配:代码中使用了针对旧版
NavigationView的.introspect(.navigationView(style: .stack)),但当前项目用的是iOS16+的NavigationStack,导致Introspect无法定位到对应的导航控制器,配置代码完全无效。
解决方案
方案一:替换.toolbarBackground(.hidden),避免破坏全局样式
如果只是需要导航栏和TabBar在Map页面视觉上隐藏,不需要完全移除背景配置,可以改用透明背景替代,这样不会覆盖全局字体设置:
// 替换原来的toolbarBackground(.hidden) .toolbarBackground(Color.clear, for: .tabBar) .toolbarBackground(Color.clear, for: .navigationBar) // 如需调整文字颜色,可添加颜色方案设置 .toolbarColorScheme(.dark, for: .navigationBar)
方案二:针对NavigationStack使用正确的Introspect配置
确保Introspect的目标是NavigationStack,修改后的代码如下:
// 在MapView的根视图添加该代码 .introspect(.navigationStack, on: .iOS(.v16, .v17)) { navigationController in let navbarAppearance = UINavigationBarAppearance() navbarAppearance.configureWithTransparentBackground() // 大标题字体配置 navbarAppearance.largeTitleTextAttributes = [ .font : UIFont(name: "Poppins", size: 38)!, NSAttributedString.Key.foregroundColor : UIColor(named: "Dark")! ] // 小标题字体配置 navbarAppearance.titleTextAttributes = [ .font : UIFont(name: "Poppins", size: 20)!, NSAttributedString.Key.foregroundColor : UIColor(named: "Dark")! ] navbarAppearance.shadowColor = .clear navbarAppearance.backgroundColor = UIColor(named: "White") // 将配置应用到当前导航控制器的导航栏 navigationController.navigationBar.standardAppearance = navbarAppearance navigationController.navigationBar.scrollEdgeAppearance = navbarAppearance navigationController.navigationBar.compactAppearance = navbarAppearance navigationController.navigationBar.isTranslucent = false }
注意:需要确保你的SwiftUI-Introspect版本支持.navigationStack的探测。
方案三:直接在SwiftUI中自定义导航栏标题
如果不需要全局统一字体,也可以直接通过自定义标题视图设置字体,不受.toolbarBackground影响:
.navigationBarTitleDisplayMode(.large) .toolbar { ToolbarItem(placement: .principal) { Text("Map") .font(.custom("Poppins", size: 38)) .foregroundColor(Color("Dark")) } }
完整修改后代码
import SwiftUI import MapKit import Introspect // 需导入Introspect库 struct RootController: View { init() { // Initialise Firebase //FirebaseApp.configure() // this is not the same as manipulating the proxy directly let appearance = UINavigationBarAppearance() // this overrides everything you have set up earlier. appearance.configureWithTransparentBackground() // this only applies to big titles appearance.largeTitleTextAttributes = [ .font : UIFont(name: "Times", size: 38)!, NSAttributedString.Key.foregroundColor : UIColor.red ] // this only applies to small titles appearance.titleTextAttributes = [ .font : UIFont(name: "Times", size: 20)!, NSAttributedString.Key.foregroundColor : UIColor.red ] let back = UIBarButtonItemAppearance() back.normal.backgroundImage = UIImage(named: "arrow.left") back.normal.titleTextAttributes = [ .font : UIFont(name: "Times", size: 20)!, NSAttributedString.Key.foregroundColor : UIColor.red ] appearance.backButtonAppearance = back appearance.shadowColor = .clear #if !targetEnvironment(macCatalyst) appearance.backgroundColor = UIColor.white #endif //In the following two lines you make sure that you apply the style for good UINavigationBar.appearance().scrollEdgeAppearance = appearance UINavigationBar.appearance().standardAppearance = appearance // This property is not present on the UINavigationBarAppearance // object for some reason and you have to leave it til the end UINavigationBar.appearance().tintColor = UIColor.black UINavigationBar.appearance().prefersLargeTitles = true UITextView.appearance().backgroundColor = .clear UINavigationBar.appearance().isTranslucent = false UIToolbar.appearance().backgroundColor = UIColor.white UIToolbar.appearance().isTranslucent = false UITabBar.appearance().barTintColor = UIColor.white UITabBar.appearance().backgroundColor = UIColor.white UITabBar.appearance().shadowImage = UIImage() UITabBar.appearance().backgroundImage = UIImage() } var body: some View { TabView { ContentView() .tabItem { Label("Home", systemImage: "house") } NavigationStack { VStack { Image(systemName: "globe") .imageScale(.large) .foregroundStyle(.tint) Text("Hello, world!") } .padding() } .tabItem { Label("Account", systemImage: "person") } } .background(Color.white) // Set tab view background explicitly } } #Preview { RootController() } struct ContentView: View { @State var position = MapCameraPosition.region( MKCoordinateRegion( center: CLLocationCoordinate2D(latitude: 51.510357, longitude: -0.116773), latitudinalMeters: 500, longitudinalMeters: 500) ) var body: some View { NavigationStack() { ScrollView() { NavigationLink(destination: MapView(position: $position)) { Text("See Map") .frame(maxWidth: .infinity, alignment: .center) .padding(.top, 400) .padding(.bottom, 600) } } .frame(maxWidth: .infinity, alignment: .center) .navigationTitle("Content") .navigationBarTitleDisplayMode(.large) } } } struct MapView: View { @Binding var position: MapCameraPosition var body: some View { ZStack() { ZStack() { Color(.white).ignoresSafeArea() Rectangle().blendMode(.destinationOut) } .compositingGroup() Map(position: $position) .mapControlVisibility(.hidden) .edgesIgnoringSafeArea(.bottom) .allowsHitTesting(false) .frame(maxWidth: .infinity) .frame(height: 414) .clipped() .padding() } .toolbarBackground(.hidden, for: .tabBar) .toolbarBackground(.hidden, for: .navigationBar) .navigationTitle("Map") .navigationBarTitleDisplayMode(.large) // 添加正确的Introspect配置 .introspect(.navigationStack, on: .iOS(.v16, .v17)) { navController in let appearance = UINavigationBarAppearance() appearance.configureWithTransparentBackground() appearance.largeTitleTextAttributes = [ .font : UIFont(name: "Times", size: 38)!, NSAttributedString.Key.foregroundColor : UIColor.red ] appearance.titleTextAttributes = [ .font : UIFont(name: "Times", size: 20)!, NSAttributedString.Key.foregroundColor : UIColor.red ] appearance.shadowColor = .clear appearance.backgroundColor = UIColor.white navController.navigationBar.standardAppearance = appearance navController.navigationBar.scrollEdgeAppearance = appearance navController.navigationBar.isTranslucent = false } } }
内容的提问来源于stack exchange,提问作者RileyDev
相关产品推荐
相关产品推荐

