【问题标题】:Dynamically change and update color of Navigation Bar in SwiftUI在 SwiftUI 中动态更改和更新导航栏的颜色
【发布时间】:2021-11-14 14:59:24
【问题描述】:

我正在尝试更改 SwiftUI 中的 NavigationBar 背景颜色,我已使用以下代码完成。但是,我还没有弄清楚如何动态更改和更新导航栏的背景颜色。

struct ContentView: View {
    @State var color: Color = .red
    
    init() {
        let navbarAppearance = UINavigationBarAppearance()
        navbarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
        navbarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
        navbarAppearance.backgroundColor = UIColor(color)
        UINavigationBar.appearance().standardAppearance = navbarAppearance
        UINavigationBar.appearance().compactAppearance = navbarAppearance
        UINavigationBar.appearance().scrollEdgeAppearance = navbarAppearance
        
    }
    
    var body: some View {
        NavigationView {
                VStack(spacing: 20) {
                    Button(action: { color = .blue }) {
                        Text("Blue")
                            .font(.title)
                            .bold()
                            .foregroundColor(.white)
                            .frame(width: 100)
                            .padding()
                            .background(Color.blue)
                            .cornerRadius(15)
                    }
                    Button(action: { color = .red }) {
                        Text("Red")
                            .font(.title)
                            .bold()
                            .foregroundColor(.white)
                            .frame(width: 100)
                            .padding()
                            .background(Color.red)
                            .cornerRadius(15)
                    }
                }
                .offset(y: -50)
                .navigationTitle("My Navigation")
        }
    }
    
}

这段代码给了我正确的结果,但是点击其中一个按钮来更改Color 变量不会更新NavigationBar 的颜色。这是因为在初始化时导航栏保持了它的所有特征,所以我需要找到一种方法来改变这些,如果可能的话,在改变颜色之间为这个过渡设置动画。感谢您的帮助!

【问题讨论】:

    标签: swift swiftui uinavigationbar swiftui-navigationview uinavigationbarappearance


    【解决方案1】:

    你可以使用SwiftUI-Introspect,所以你只是改变了这个NavigationView的导航栏。它不会影响任何其他实例。

    您也不会使用.id(...),这可能很糟糕,因为当视图更改身份时,它可能会破坏动画、不必要地重新初始化视图、破坏视图生命周期等。

    在我的示例中,您保存UINavigationController 的当前实例。当color的值发生变化时,导航栏的外观重新设置。

    内省示例:

    import Introspect
    
    /* ... */
    
    struct ContentView: View {
        @State private var color: Color = .red
        @State private var nav: UINavigationController?
    
        var body: some View {
            NavigationView {
                VStack(spacing: 20) {
                    Button(action: { color = .blue }) {
                        Text("Blue")
                            .font(.title)
                            .bold()
                            .foregroundColor(.white)
                            .frame(width: 100)
                            .padding()
                            .background(Color.blue)
                            .cornerRadius(15)
                    }
    
                    Button(action: { color = .red }) {
                        Text("Red")
                            .font(.title)
                            .bold()
                            .foregroundColor(.white)
                            .frame(width: 100)
                            .padding()
                            .background(Color.red)
                            .cornerRadius(15)
                    }
                }
                .offset(y: -50)
                .navigationTitle("My Navigation")
            }
            .introspectNavigationController { nav in
                self.nav = nav
                updateNavBar()
            }
            .onChange(of: color) { _ in
                updateNavBar()
            }
        }
    
        private func updateNavBar() {
            guard let nav = nav else { return }
            let navbarAppearance = UINavigationBarAppearance()
            navbarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
            navbarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
            navbarAppearance.backgroundColor = UIColor(color)
            nav.navigationBar.standardAppearance = navbarAppearance
            nav.navigationBar.compactAppearance = navbarAppearance
            nav.navigationBar.scrollEdgeAppearance = navbarAppearance
        }
    }
    

    结果:

    【讨论】:

    • 非常感谢!这绝对是我一直在寻找的,我特别喜欢这只影响我应用它的导航视图的实例。现在唯一要做的就是弄清楚我是否可以为这个过渡设置动画……如果可以的话。
    【解决方案2】:

    是的,外观应用于在外观本身之后创建的所有视图。所以我们需要重置外观,然后在颜色变化时再次创建NavigationView

    这是一个可能方法的演示(使用 Xcode 13 / iOS 15 测试)

    struct ContentView: View {
        @State var color: Color = .red
    
        var body: some View {
            MainNavView(barColor: $color)
                .id(color)                // << re-creates !!
        }
    }
    
    struct MainNavView: View {
        @Binding var color: Color
        init(barColor: Binding<Color>) {
            self._color = barColor
            let navbarAppearance = UINavigationBarAppearance()
            navbarAppearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
            navbarAppearance.titleTextAttributes = [.foregroundColor: UIColor.white]
            navbarAppearance.backgroundColor = UIColor(color)
            UINavigationBar.appearance().standardAppearance = navbarAppearance
            UINavigationBar.appearance().compactAppearance = navbarAppearance
            UINavigationBar.appearance().scrollEdgeAppearance = navbarAppearance
    
        }
    
        var body: some View {
            NavigationView {
                VStack(spacing: 20) {
                    Button(action: { color = .blue }) {
                        Text("Blue")
                            .font(.title)
                            .bold()
                            .foregroundColor(.white)
                            .frame(width: 100)
                            .padding()
                            .background(Color.blue)
                            .cornerRadius(15)
                    }
                    Button(action: { color = .red }) {
                        Text("Red")
                            .font(.title)
                            .bold()
                            .foregroundColor(.white)
                            .frame(width: 100)
                            .padding()
                            .background(Color.red)
                            .cornerRadius(15)
                    }
                }
                .offset(y: -50)
                .navigationTitle("My Navigation")
            }
        }
    }
    

    【讨论】:

    • 感谢您提供此解决方案,但我在将新视图推送到导航堆栈时遇到问题,因此我采用了不同的方法。
    猜你喜欢
    • 2019-10-23
    • 1970-01-01
    • 2018-02-06
    • 1970-01-01
    • 2020-01-21
    • 1970-01-01
    • 2014-07-05
    相关资源
    最近更新 更多