Change Navigation Text Title Color on Different SwiftUI Views

Hi

I reviewed this post: How to change navigation title color in swiftUI

This seems to work fine if the intention is to apply the title color across an application. I would like to apply a different text title color selectively depending on the View being shown. And in some instances revert back to the color depending on the light and dark themes.

Same result occurs using a viewmodifier or simply using onAppear and onDisappear with the title color is applied to all views. And if you do modify it in onDisappear, when you navigate back to another view which changes the color onAppear it has the same color as the previous view.

The only way I've found this to work is using UIViewControllerRepresentable and handling the viewWillAppear and viewWillDisappear something like this:

NavigationBarView(
   viewWillAppear: { nav in
      nav.navigationBar.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
   },
   viewWillDisappear: { nav in
      nav.navigationBar.largeTitleTextAttributes = nil
   }
)

Has anyone been successful in getting a different title text color to apply to different views using a modifier or onAppear and onDisappear?

Appreciate any guidance.

Replies

Hi

I refactored my NavigationBarView and was able to me more "SwiftUI" like in the implementation. Hope this helps:

import SwiftUI

extension View {
    func onWillDisappear(_ perform: @escaping () -> Void) -> some View {
        modifier(ViewWillDisappearModifier(callback: perform))
    }
    
    func onWillAppear(_ perform: @escaping () -> Void) -> some View {
        modifier(ViewWillAppearModifier(callback: perform))
    }
}

struct ViewWillDisappearModifier: ViewModifier {
    let callback: () -> Void
    
    func body(content: Content) -> some View {
        content.background(UIViewControllerResponder(viewWillDisappear: callback))
    }
}

struct ViewWillAppearModifier: ViewModifier {
    let callback: () -> Void
    
    func body(content: Content) -> some View {
        content.background(UIViewControllerResponder(viewWillAppear: callback))
    }
}

private struct UIViewControllerResponder: UIViewControllerRepresentable {

    /// Notifies the view controller that its view is about to be added to a view hierarchy.
    var viewWillAppear: (() -> Void)? = nil
    
    /// Notifies the view controller that its view is about to be removed from a view hierarchy.
    var viewWillDisappear: (() -> Void)? = nil
    
    private let controller = ViewController()

    func makeUIViewController(context: UIViewControllerRepresentableContext<UIViewControllerResponder>) -> UIViewController {
        if let viewWillAppear = viewWillAppear {
            controller.viewWillAppear = viewWillAppear
        }
        
        if let viewWillDisappear = viewWillDisappear {
            controller.viewWillDisappear = viewWillDisappear
        }
        
        return controller
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: UIViewControllerRepresentableContext<UIViewControllerResponder>) {
    }


    /// An object that manages a view hierarchy for your UIKit app.
    private class ViewController: UIViewController {
        var viewWillAppear: () -> Void = { }
        var viewWillDisappear: () -> Void = { }
        
        /// Notifies the view controller that its view is about to be added to a view hierarchy.
        /// - Parameter animated: If true, the view is being added to the window using an animation.
        override func viewWillAppear(_ animated: Bool) {
            super.viewWillAppear(animated)
            viewWillAppear()
            
        }

        /// /// Notifies the view controller that its view is about to be removed from a view hierarchy.
        /// - Parameter animated: If true, the view is being added to the window using an animation.
        override func viewWillDisappear(_ animated: Bool) {
            super.viewWillDisappear(animated)
            viewWillDisappear()
        }
    }
}

And in the View, this is how you interact with the events:

struct MyView: View {
    var body: some View {
        NavigationStack {
                Text("Home")
                    .navigationTitle("Some Text")
                    .toolbar {
                        ToolbarItem(placement: .primaryAction) {
                            Button {
                                print("Toolbar button pressed")
                            }
                        label: {
                            Image(systemName: "plus")
                        }
                    }
                }
        }
        .onWillAppear {
            let appearance = UINavigationBarAppearance()
            appearance.titleTextAttributes = [.foregroundColor: UIColor.white]
            appearance.largeTitleTextAttributes = [.foregroundColor: UIColor.white]
            UINavigationBar.appearance().standardAppearance = appearance
            UINavigationBar.appearance().compactAppearance = appearance
        }
        .onWillDisappear {
            let appearance = UINavigationBarAppearance()
            appearance.titleTextAttributes = [:]
            appearance.largeTitleTextAttributes = [:]
            UINavigationBar.appearance().standardAppearance = appearance
            UINavigationBar.appearance().compactAppearance = appearance
        }
    }
}