Reputation: 413
Goal: Update Navigation Bar color on press of a button.
Current: NavBar Color doesn't change at all.
Attempted: I am currently using an init() to set the navBarTintColor & I have also tried this answer but neither will update on demand.
Code:
var navColor = Color.green
struct ContentView: View {
@State var themeColor = Color.green
@State var items = [0,1,2,3,4,5,6,7,8,9]
@State var showSheet = false
init() {
UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.black]
UINavigationBar.appearance().barTintColor = UIColor(navColor)
UINavigationBar.appearance().backgroundColor = UIColor(navColor)
}
var body: some View {
NavigationView{
List{
ForEach(0..<items.count){ i in
Text("Item: \(items[i])")
}.listRowBackground(themeColor)
}
.navigationBarTitle("Theme", displayMode: .inline)
.navigationBarItems(leading: Button(action: {
showSheet.toggle()
}, label: {
Text("Sheet")
})
,trailing: Button(action: {
self.themeColor = Color(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1))
navColor = Color(red: .random(in: 0...1), green: .random(in: 0...1), blue: .random(in: 0...1))
items.shuffle()
}, label: {
Text("Random Color")
}))
// .background(NavigationConfigurator { nc in
// nc.navigationBar.barTintColor = UIColor(navColor)
// nc.navigationBar.titleTextAttributes = [.foregroundColor : UIColor.black]
// })
.sheet(isPresented: $showSheet, content: {
popSheet()
})
}
}
}
struct popSheet: View {
var body: some View{
NavigationView{
Text("Hello")
.navigationBarTitle("Sheet", displayMode: .inline)
}
}
}
Here is the navigation configurator I also attempted to use.
struct NavigationConfigurator: UIViewControllerRepresentable {
var configure: (UINavigationController) -> Void = { _ in }
func makeUIViewController(context: UIViewControllerRepresentableContext<NavigationConfigurator>) -> UIViewController {
UIViewController()
}
func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<NavigationConfigurator>) {
if let nc = uiViewController.navigationController {
self.configure(nc)
}
}
}
Upvotes: 6
Views: 8840
Reputation: 160
There's a new modifier that make this much easier: .toolbarBackground
. To use it, place it on a view within a NavigationStack
(not on the stack itself, similar to .navigationTitle
). Just assign to it the color set up in the state variable, and have the buttons update the variable, like so:
struct ContentView: View {
@State var themeColor = Color.green
var body: some View {
NavigationStack {
VStack {
Button("Change to blue") {
themeColor = .blue
}
Button("Change to red") {
themeColor = .red
}
}
.navigationTitle("Title")
.toolbarBackground(themeColor, for: .navigationBar)
.toolbarBackground(.visible, for: .navigationBar)
}
}
}
Note: the visibility modifier is necessary, as the default behavior is to only show the changed color when scrolling.
Upvotes: 4
Reputation: 54466
You can force refresh the NavigationView
using .id(themeColor)
.
Here is a possible solution:
struct ContentView: View {
@State var themeColor = Color.green
init() {
updateNavigationBarColor()
}
var body: some View {
NavigationView {
VStack {
Button("Change to blue") {
themeColor = .blue
updateNavigationBarColor()
}
Button("Change to red") {
themeColor = .red
updateNavigationBarColor()
}
}
.navigationTitle("Title")
}
.id(themeColor)
}
func updateNavigationBarColor() {
UINavigationBar.appearance().barTintColor = UIColor(themeColor)
UINavigationBar.appearance().backgroundColor = UIColor(themeColor)
}
}
Upvotes: 11