You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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
    
}
原因分析
  1. .toolbarBackground(.hidden)的重置作用:该修改器会直接覆盖导航栏的全局外观配置,包括之前通过UINavigationBar.appearance()设置的字体属性,强制恢复系统默认样式。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 22:43:11