1

I need to be able to change the colors in a SwiftUI View on demand. This is not an issue for switching colors on demand for objects in a View, but how would one be able to do that for the NavigationBar appearance properties? Here is how I set the Navigation Bar appearance when the view is initialized. Tapping the button changes the button colors, but not the Nav Bar appearance. Restarting the app with a different value for theme1 will show the correct colors, but tapping the button only changes the button colors and not the NavBar appearance.

struct ContentView: View {
@State private var theme1 = true
init() {
    let navBarAppearance = UINavigationBarAppearance()
    navBarAppearance.titleTextAttributes = [.foregroundColor: theme1 ? UIColor.red : UIColor.yellow]
    navBarAppearance.largeTitleTextAttributes = [.foregroundColor: theme1 ? UIColor.red : UIColor.yellow]
    navBarAppearance.backgroundColor = theme1 ? UIColor.yellow : UIColor.red
    UINavigationBar.appearance().standardAppearance = navBarAppearance
    UINavigationBar.appearance().compactAppearance = navBarAppearance
    UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
    UINavigationBar.appearance().tintColor = theme1 ? UIColor.red : UIColor.yellow
}
var body: some View {
    NavigationView {
        VStack {
            Button("Toggle Style") {
                theme1.toggle()
            }
            .padding(8)
            .foregroundColor(theme1 ? Color(.red): Color(.yellow))
            .background(RoundedRectangle(cornerRadius: 10)
                            .fill(theme1 ? Color(.yellow) : Color(.red)))
        }
        .navigationTitle("Theme Picker")
    }
}

}

Stewart Lynch
  • 875
  • 9
  • 26

1 Answers1

1

Because we are using UIKit in code, we have to kill View First then create the one we want:


Version 1:

import SwiftUI

struct ContentView: View {
    @State private var theme1 = true

    var body: some View {
        
        if theme1 {
            CustomView(theme: $theme1)
        }
        else {
            CustomView(theme: $theme1)
        }
  
    }
}

struct CustomView: View {
    
    @Binding var theme1: Bool
    
    init(theme: Binding<Bool>) {
        
        _theme1 = theme
        
        let navBarAppearance = UINavigationBarAppearance()
        navBarAppearance.titleTextAttributes = [.foregroundColor: theme1 ? UIColor.red : UIColor.yellow]
        navBarAppearance.largeTitleTextAttributes = [.foregroundColor: theme1 ? UIColor.red : UIColor.yellow]
        navBarAppearance.backgroundColor = theme1 ? UIColor.yellow : UIColor.red
        UINavigationBar.appearance().standardAppearance = navBarAppearance
        UINavigationBar.appearance().compactAppearance = navBarAppearance
        UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
        UINavigationBar.appearance().tintColor = theme1 ? UIColor.red : UIColor.yellow
    }
    
    var body: some View {
        NavigationView {
            VStack {
                Button("Toggle Style") {
                    theme1.toggle()
                }
                .padding(8)
                .foregroundColor(theme1 ? Color(.red): Color(.yellow))
                .background(RoundedRectangle(cornerRadius: 10)
                                .fill(theme1 ? Color(.yellow) : Color(.red)))
            }
            .navigationTitle("Theme Picker")
        }
    }
    
}

Version 2:

This is a way that I would recommend: With this in mind that we should be responsible for navigationTitle ourself, like when it should be shown or hide or be small . . .


import SwiftUI

struct ContentView: View {
    
    @State private var theme1 = true
    
    var body: some View {
        
        NavigationView {
            
            ZStack {
                
                theme1 ? Color.yellow.ignoresSafeArea() : Color.red.ignoresSafeArea()
                
                Color.white.cornerRadius(20).padding()
                
                VStack {
                    
                    Button(action: { theme1.toggle() }, label: {
                        Text("Toggle Style")
                            .bold()
                            .padding(8)
                            .background(theme1 ? Color.yellow : Color.red)
                            .cornerRadius(10)
                    })
                    
                }
                
            }
            
        }
        .overlay(navigationTitle, alignment: .topLeading)
        .foregroundColor(theme1 ? Color.red : Color.yellow)
        .accentColor(theme1 ? Color.red : Color.yellow)
        
    }
    
    var navigationTitle: some View {
        
        return Text("Theme Picker").font(Font.largeTitle.bold()).padding().offset(y: 30)
    }

}
ios coder
  • 1
  • 4
  • 31
  • 91
  • Thanks. I actually prefer the 1st way, though it is still not ideal. Thanks very much for taking the time for this detailed response. – Stewart Lynch Mar 28 '21 at 04:20
  • @StewartLynch: You are absolutely welcome, I just recommended second way because of pure SwiftUI, also I am big fan of your teaching style and I always recommend your youtube channel for anyOne who ask a good place for learning Swift/SwiftUI, So thank you for your hard works! – ios coder Mar 28 '21 at 09:11
  • Thanks for your comment. As a matter of fact, I am putting together a tutorial on theming an application in SwiftUI so this was one of the missing pieces. When I do the video, if it is OK with you, I would like to credit you with this solution as I am going to be using it. If you let me know your twitter handle, I will make sure you are credited in the video. – Stewart Lynch Mar 28 '21 at 19:48
  • @StewartLynch: That is so nice of you, just enjoyed watching today's video about **Package**, I have no twitter related to Swift programming, I am just fun programmer. it is your decision wether you say about me or not, there is no copy right or some thing on any thing I share via StackOverFlow, but personally I would be happy to hear my name from my teacher. If you think it makes that Video better do it, otherwise I am fine without out mentioning as well. – ios coder Mar 28 '21 at 20:09
  • @StewartLynch: I just answered another question with same way here: https://stackoverflow.com/a/67096930/14998134 – ios coder Apr 15 '21 at 20:57
  • Thanks. I have done a video on this. Still waiting for release though since I have a number of others already backed up. Probably be released some time in May before WWDC when everything will change again :) – Stewart Lynch Apr 18 '21 at 23:12
  • Perfect! As we now this kind of issue are mostly temporary and may get fixed with new version of Xcode, I just tried to make a custom element of apple elements that we have issue with them, and in the case of solving issue from Apple we could just replace our custom way with original way. Big thanks to you for helping us! – ios coder Apr 19 '21 at 02:27