点击按钮后如何刷新Navigation Bar背景颜色视图?
问题描述
我在SwiftUI应用中实现了主题切换功能,用户点击按钮就能全局切换主题色,其他视图都能正常更新,但设置导航栏背景色的NavigationViewBackgroundColor视图无法随主题刷新,必须重启应用才会生效。我试过用@State变量强制更新,但没用。
导航栏设置代码
import SwiftUI struct NavigationViewBackgroundColor: View { // THEME @ObservedObject var theme = ThemeSettings.shared var themes: [Theme] = themeData @Environment(\.refresh) private var refresh init() { let coloredAppearance = UINavigationBarAppearance() coloredAppearance.configureWithOpaqueBackground() coloredAppearance.backgroundColor = UIColor(Color(themes[self.theme.themeSettings].themePrimaryColor))//.darkGray coloredAppearance.titleTextAttributes = [.foregroundColor: UIColor(Color(themes[self.theme.themeSettings].themeSecondaryColor))] coloredAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor(Color(themes[self.theme.themeSettings].themeSecondaryColor))] UINavigationBar.appearance().standardAppearance = coloredAppearance UINavigationBar.appearance().compactAppearance = coloredAppearance UINavigationBar.appearance().scrollEdgeAppearance = coloredAppearance UINavigationBar.appearance().tintColor = .white UINavigationBar.appearance().frame = CGRect(x: 0, y: 0, width: 400, height: 4) } //:END OF INIT var body: some View { NavigationView { } //:END OF NAVIGATION VIEW .navigationBarTitle("",displayMode: .inline) .navigationBarHidden(true) } //:END OF BODY/VIEW } //:END OF STRUT struct NavigationViewBackgroundColor_Previews: PreviewProvider { static var previews: some View { NavigationViewBackgroundColor() } }
AppView代码
import SwiftUI struct AppView: View { // MARK: - PROPERTIES // THEME @ObservedObject var theme = ThemeSettings.shared var themes: [Theme] = themeData // MARK: - BODY var body: some View { NavigationView { VStack { ZStack { Text("Navigation View") .foregroundColor(Color(themes[self.theme.themeSettings].themePrimaryColor)) } //: END OF ZSTACK } //: END OF BODY VSTACK .edgesIgnoringSafeArea(.all) .accentColor(Color(themes[self.theme.themeSettings].themeSecondaryColor)) .background(Color(themes[self.theme.themeSettings].themePrimaryColor)) .navigationBarTitle("",displayMode: .inline) .navigationBarItems(trailing: NavigationViewBackgroundColor()) } //: END OF NAVIGATION VIEW .navigationViewStyle(StackNavigationViewStyle()) .onAppear { //:THIS IS ONLY FOR TESTING REMOVE BEFORE LAUNCH if UserDefaults.standard.bool(forKey: "Theme") { UserDefaults.standard.removeObject(forKey: "Theme") UserDefaults.standard.set(0, forKey: "Theme") } else { UserDefaults.standard.set(0, forKey: "Theme") } //:END OF IF FOR THEME THIS WILL SET A DEFAULT } //:END OF ONAPPEAR } }
主题数据代码
// 模型 import SwiftUI // MARK: - THEME MODEL struct Theme: Identifiable { let id: Int let themeName: String let themePrimaryColor: String let themePrimaryLightColor: String let themeSecondaryColor: String let themeAccentColor: String } // 数据 import SwiftUI // MARK: - THEME DATA let themeData: [Theme] = [ Theme(id: 0, themeName: "Pink Theme", themePrimaryColor: "Theme-Pink-Primary-Color", themePrimaryLightColor: "Theme-Pink-Primary-Light-Color", themeSecondaryColor: "Theme-Pink-Secondary-Color", themeAccentColor: "Theme-Pink-Accent-Color"), Theme(id: 1, themeName: "Blue Theme", themePrimaryColor: "Theme-Blue-Primary-Color", themePrimaryLightColor: "Theme-Blue-Primary-Light-Color",themeSecondaryColor: "Theme-Blue-Secondary-Color", themeAccentColor: "Theme-Blue-Accent-Color"), Theme(id: 2, themeName: "Green Theme", themePrimaryColor: "Theme-Green-Primary-Color", themePrimaryLightColor: "Theme-Green-Primary-Light-Color",themeSecondaryColor: "Theme-Green-Secondary-Color", themeAccentColor: "Theme-Green-Accent-Color"), Theme(id: 3, themeName: "Orange Theme", themePrimaryColor: "Theme-Orange-Primary-Color", themePrimaryLightColor: "Theme-Orange-Primary-Light-Color",themeSecondaryColor: "Theme-Orange-Secondary-Color", themeAccentColor: "Theme-Orange-Accent-Color") ] // 捕获颜色选择的可观察对象 import Foundation import SwiftUI // MARK: - THEME CLASS final public class ThemeSettings: ObservableObject { @Published public var themeSettings: Int = UserDefaults.standard.integer(forKey: "Theme") { didSet { UserDefaults.standard.set(self.themeSettings, forKey: "Theme") } //:END OF SET } //:END OF PUBLISHED private init() {} public static let shared = ThemeSettings() } //:END OF CLASS
解决方案
问题核心是你把导航栏外观配置写在了init方法里,init仅在视图初始化时执行一次,后续主题变化不会触发重新配置。要实现实时更新,需要把配置逻辑移到能响应theme变化的回调中。
修改后的NavigationViewBackgroundColor代码
import SwiftUI struct NavigationViewBackgroundColor: View { @ObservedObject var theme = ThemeSettings.shared var themes: [Theme] = themeData var body: some View { // 用EmptyView替代多余的NavigationView,仅负责配置逻辑 EmptyView() .onChange(of: theme.themeSettings) { _ in updateNavigationBarAppearance() } .onAppear { // 初始化时加载主题配置 updateNavigationBarAppearance() } } // 抽离导航栏配置逻辑,便于复用和维护 private func updateNavigationBarAppearance() { let currentTheme = themes[theme.themeSettings] let coloredAppearance = UINavigationBarAppearance() coloredAppearance.configureWithOpaqueBackground() coloredAppearance.backgroundColor = UIColor(Color(currentTheme.themePrimaryColor)) coloredAppearance.titleTextAttributes = [.foregroundColor: UIColor(Color(currentTheme.themeSecondaryColor))] coloredAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor(Color(currentTheme.themeSecondaryColor))] UINavigationBar.appearance().standardAppearance = coloredAppearance UINavigationBar.appearance().compactAppearance = coloredAppearance UINavigationBar.appearance().scrollEdgeAppearance = coloredAppearance UINavigationBar.appearance().tintColor = .white // 移除手动设置的frame,导航栏尺寸由系统自动管理 } } struct NavigationViewBackgroundColor_Previews: PreviewProvider { static var previews: some View { NavigationViewBackgroundColor() } }
关键优化说明
- 移除冗余NavigationView:原视图中的
NavigationView完全多余,替换为EmptyView即可,因为我们只需要执行配置逻辑,不需要渲染额外视图。 - 监听主题变化:通过
.onChange(of: theme.themeSettings)监听主题切换事件,实时触发导航栏外观更新。 - 初始化配置:在
.onAppear中调用配置方法,确保应用启动时主题立即生效。 - 抽离配置方法:将导航栏配置逻辑单独封装,提升代码可读性和可维护性。
- 移除无效frame设置:手动设置导航栏frame可能导致布局异常,系统会自动管理导航栏尺寸。
修正AppView语法错误
原代码中navigationBarItems缺少位置参数,修正为:
.navigationBarItems(trailing: NavigationViewBackgroundColor())
修改完成后,主题切换时导航栏会实时更新样式,无需重启应用。
内容的提问来源于stack exchange,提问作者brandon aho
相关产品推荐
相关产品推荐

