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

点击按钮后如何刷新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()
    }
}

关键优化说明

  1. 移除冗余NavigationView:原视图中的NavigationView完全多余,替换为EmptyView即可,因为我们只需要执行配置逻辑,不需要渲染额外视图。
  2. 监听主题变化:通过.onChange(of: theme.themeSettings)监听主题切换事件,实时触发导航栏外观更新。
  3. 初始化配置:在.onAppear中调用配置方法,确保应用启动时主题立即生效。
  4. 抽离配置方法:将导航栏配置逻辑单独封装,提升代码可读性和可维护性。
  5. 移除无效frame设置:手动设置导航栏frame可能导致布局异常,系统会自动管理导航栏尺寸。

修正AppView语法错误

原代码中navigationBarItems缺少位置参数,修正为:

.navigationBarItems(trailing: NavigationViewBackgroundColor())

修改完成后,主题切换时导航栏会实时更新样式,无需重启应用。

内容的提问来源于stack exchange,提问作者brandon aho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:45:36