Swiftui Update Navigation Bar Title Color

SwiftUI update navigation bar title color

It is not necessary to use .appearance() to do this globally.

Although SwiftUI does not expose navigation styling directly, you can work around that by using UIViewControllerRepresentable. Since SwiftUI is using a regular UINavigationController behind the scenes, the view controller will still have a valid .navigationController property.

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)
}
}

}

And to use it

struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
Text("Don't use .appearance()!")
}
.navigationBarTitle("Try it!", displayMode: .inline)
.background(NavigationConfigurator { nc in
nc.navigationBar.barTintColor = .blue
nc.navigationBar.titleTextAttributes = [.foregroundColor : UIColor.white]
})
}
.navigationViewStyle(StackNavigationViewStyle())
}
}

Modified navigation bar

Change navigation title text color SwiftUI

You can do it with init()

init() {
//Use this if NavigationBarTitle is with Large Font
UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor: UIColor.red]

//Use this if NavigationBarTitle is with displayMode = .inline
UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.red]
}

Full Code

struct YourView: View {

init() {
//Use this if NavigationBarTitle is with Large Font
UINavigationBar.appearance().largeTitleTextAttributes = [.foregroundColor: UIColor.red]

//Use this if NavigationBarTitle is with displayMode = .inline
UINavigationBar.appearance().titleTextAttributes = [.foregroundColor: UIColor.red]
}

var body: some View {

NavigationView {
List{
Text("1")
}
}
.navigationBarTitle("TEST")

}
}
}

SwiftUI: Update Navigation Bar Color

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)
}
}

SwiftUI changing navigation bar background color for inline navigationBarTitleDisplayMode

I think I have what you want. It is VERY touchy... It is a hack, and not terribly robust, so take as is...

I got it to work by having your modifier return a clear NavBar, and then the solution from this answer works for you. I even added a ScrollView to ThirdView() to make sure that scrolling under didn't affect in. Also note, you lose all of the other built in effects of the bar like translucency, etc.

Edit: I went over the code. The .navigationViewStyle was in the wrong spot. It likes to be outside of the NavigaionView(), where everything else needs to be inside. Also, I removed the part of the code setting the bar color in FirstView() as it was redundant and ugly. I hadn't meant to leave that in there.

struct NavigationBarModifier: ViewModifier {

var backgroundColor: UIColor?
var titleColor: UIColor?


init(backgroundColor: Color, titleColor: UIColor?) {
self.backgroundColor = UIColor(backgroundColor)

let coloredAppearance = UINavigationBarAppearance()
coloredAppearance.configureWithTransparentBackground()
coloredAppearance.backgroundColor = .clear // The key is here. Change the actual bar to clear.
coloredAppearance.titleTextAttributes = [.foregroundColor: titleColor ?? .white]
coloredAppearance.largeTitleTextAttributes = [.foregroundColor: titleColor ?? .white]
coloredAppearance.shadowColor = .clear

UINavigationBar.appearance().standardAppearance = coloredAppearance
UINavigationBar.appearance().compactAppearance = coloredAppearance
UINavigationBar.appearance().scrollEdgeAppearance = coloredAppearance
UINavigationBar.appearance().tintColor = titleColor
}

func body(content: Content) -> some View {
ZStack{
content
VStack {
GeometryReader { geometry in
Color(self.backgroundColor ?? .clear)
.frame(height: geometry.safeAreaInsets.top)
.edgesIgnoringSafeArea(.top)
Spacer()
}
}
}
}
}

extension View {
func navigationBarColor(backgroundColor: Color, titleColor: UIColor?) -> some View {
self.modifier(NavigationBarModifier(backgroundColor: backgroundColor, titleColor: titleColor))
}
}

struct FirstView: View {
@State private var selection: String? = nil

var body: some View {
NavigationView {
GeometryReader { _ in
VStack {
Text("This is the first view")

NavigationLink(destination: SecondView(), tag: "SecondView", selection: $selection) {
EmptyView()
}
Button(action: {
self.selection = "SecondView"
print("Go to second view")
}) {
Text("Go to second view")
}
}
.navigationTitle("First")
.navigationBarTitleDisplayMode(.inline)
.navigationBarColor(backgroundColor: .red, titleColor: .black)
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}


struct SecondView: View {
@State private var selection: String? = nil

var body: some View {
VStack {
Text("This is the second view")

NavigationLink(destination: ThirdView(), tag: "ThirdView", selection: $selection) {
EmptyView()
}
Button(action: {
self.selection = "ThirdView"
print("Go to third view")
}) {
Text("Go to third view")
}
}
.navigationTitle("Second")
.navigationBarTitleDisplayMode(.inline)
.navigationBarColor(backgroundColor: .blue, titleColor: .black)
}
}

struct ThirdView: View {
var body: some View {
ScrollView {
ForEach(0..<50) { _ in
Text("This is the third view")
}
}
.navigationTitle("Third")
.navigationBarTitleDisplayMode(.inline)
.navigationBarColor(backgroundColor: .green, titleColor: .black)
}
}

Dynamically change and update color of Navigation Bar in SwiftUI

You can use SwiftUI-Introspect, so you are only changing the navigation bar of this NavigationView. It will not affect any other instance.

You also won't be using .id(...), which is potentially bad because when a view changes identity it can break animations, unnecessarily reinitialize views, break view lifecycles, etc.

In my example, you save the current instance of the UINavigationController. When the value of color changes, the appearance of the navigation bar is set again.

Example with Introspect:

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
}
}

Result:

Result

SwiftUI Navigation Bar Colour

There's no available (SwiftUI) API for doing that (yet) (beta 5).

But we could use UINavigationBar.appearance(), as in:

UINavigationBar.appearance().backgroundColor = .clear

Full Code

import SwiftUI

struct JobDetailView: View {

init() {
UINavigationBar.appearance().backgroundColor = .clear
}

var body: some View {
NavigationView {
Form {
Section(header: Text("General")) {
HStack {
Text("Job Name")
Spacer()
Text("Scientist")
.multilineTextAlignment(.trailing)
}
HStack {
Text("Hourly Rate")
Spacer()
Text("$ 1.00")
.multilineTextAlignment(.trailing)
}
}

}
.navigationBarTitle("Scientist")
.navigationBarHidden(false)
}
}
}

#if DEBUG
struct JobDetailView_Previews: PreviewProvider {
static var previews: some View {
JobDetailView()
}
}
#endif

Result

result

Dark Mode Result

result dark

SwiftUI change navigation title color for current view only

The simplest case is as follows (you can also store/restore previous settings in some local var):

var body: some View {
NavigationView(){
List {
// Some content is here
}
.navigationBarTitle("Title")
.onAppear(perform: {
UINavigationBar.appearance().largeTitleTextAttributes = [
.foregroundColor: UIColor.red
]
})
.onDisappear(perform: {
UINavigationBar.appearance().largeTitleTextAttributes = nil
})

}
}

How to change navigationBar background color locally

In the place you try to get navigation controller it is not injected yet. Here is fixed configurator (tested with Xcode 12.1 / iOS 14.1):

struct UINavigationConfiguration: UIViewControllerRepresentable {
var config: (UINavigationController) -> Void = {_ in }

func makeUIViewController(context: Context) -> UIViewController {
let controller = UIViewController()
DispatchQueue.main.async {
if let nc = controller.navigationController {
self.config(nc)
}
}
return controller
}

func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
}
}


Related Topics



Leave a reply



Submit