44

This is a SwiftUI question, not a UIKit one. :)

I'm trying to set a different font for the navigation bar title using SwiftUI. My suspicion is that this isn't supported yet. Here's what I've tried:

var body: some View {
  NavigationView {
    .navigationBarTitle(Text("Dashboard").font(.subheadline), displayMode: .large)
  }
}

No matter what I do with the .font settings, it doesn't change the text. I've also tried setting a custom font and removing the displayMode property.

enter image description here

Has anyone been able to get this to work?

Anjali Kevadiya
  • 3,567
  • 4
  • 22
  • 43
Clifton Labrum
  • 13,053
  • 9
  • 65
  • 128

7 Answers7

53

In SwiftUI, at this point we can not change the navigationBarTitle font directly, but you can change navigationBar appearance like this,

struct YourView: View {
    init() {
        //Use this if NavigationBarTitle is with Large Font
        UINavigationBar.appearance().largeTitleTextAttributes = [.font : UIFont(name: "Georgia-Bold", size: 20)!]

        //Use this if NavigationBarTitle is with displayMode = .inline
        //UINavigationBar.appearance().titleTextAttributes = [.font : UIFont(name: "Georgia-Bold", size: 20)!]
    }
    var body: some View {
        NavigationView {
            Text("Hello World!")
              .navigationBarTitle(Text("Dashboard").font(.subheadline), displayMode: .large)
            //.navigationBarTitle (Text("Dashboard"), displayMode: .inline)
        }
    }
}

I hope this will help you. Thanks!!

Anjali Kevadiya
  • 3,567
  • 4
  • 22
  • 43
  • Great point, @anjali-kevadiya . One thing just -- do not need to settle `Text("...")` with font modifying as `navigationBatTitle` parameter, furthermore Xcode says in runtime it's not acceptable to apply any kind of modifications to the nav bar title itself. So you can type `.navigationBarTitle("Dashboard")` and that would be enough. – hamsternik Apr 29 '22 at 23:22
16

In iOS 14 SwiftUI you can customise a View navigation bar title with the toolbar modifier, set ToolbarItem of placement type .principal to a new toolbar modifier.

NavigationView {
    Text("any text")
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .principal) {
                VStack {
                    Text("Nav Title")
                      .font(.system(size: 20))
                      .foregroundColor(Color.black)
                }
            }
        }
}
prabhu
  • 684
  • 11
  • 27
12

If you need to use new Rounded face for SF family you can use this snippet

    let design = UIFontDescriptor.SystemDesign.rounded
    let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .largeTitle)
                                     .withDesign(design)!
    let font = UIFont.init(descriptor: descriptor, size: 48)
    UINavigationBar.appearance().largeTitleTextAttributes = [.font : font]
Sound Blaster
  • 4,778
  • 1
  • 24
  • 32
5

I'm not a huge fan of modifying things in the view's Inits however, I'm not sure of a better way. Instead, I moved it to a ViewModifier to keep things tidier:

struct SpecialNavBar: ViewModifier {

    init() {
        UINavigationBar.appearance().largeTitleTextAttributes = [.font: UIFont(name: "Georgia-Bold", size: 20)!]
    }

    func body(content: Content) -> some View {
        content
    }

}

extension View {

    func specialNavBar() -> some View {
        self.modifier(SpecialNavBar())
    }

}

Then to use:

struct MyView: View {

    var body: some View {
        NavigationView { 
            content
                .specialNavBar()
        }
    }

}
huwr
  • 1,720
  • 3
  • 19
  • 34
2

All the settings you need are inside init(). Play with them and understand what is responsible for what. A couple of hours ago, this code helped me to understand the Navigation Bar settings. I don't remember where I found it.

struct ContentView: View {
    init() {
        // this is not the same as manipulating the proxy directly
        let appearance = UINavigationBarAppearance()
        
        // this overrides everything you have set up earlier.
        appearance.configureWithTransparentBackground()
        
        // this only applies to big titles
        appearance.largeTitleTextAttributes = [
            .font : UIFont.systemFont(ofSize: 20),
            NSAttributedString.Key.foregroundColor : UIColor.white
        ]
        // this only applies to small titles
        appearance.titleTextAttributes = [
            .font : UIFont.systemFont(ofSize: 20),
            NSAttributedString.Key.foregroundColor : UIColor.white
        ]
        
        //In the following two lines you make sure that you apply the style for good
        UINavigationBar.appearance().scrollEdgeAppearance = appearance
        UINavigationBar.appearance().standardAppearance = appearance
        
        // This property is not present on the UINavigationBarAppearance
        // object for some reason and you have to leave it til the end
        UINavigationBar.appearance().tintColor = .white
        
    }
    var body: some View {
        NavigationView {
            ZStack {
                Color.black
                    .edgesIgnoringSafeArea([.all])
                NavigationLink(destination: ContentView2()) {
                    Text("push")
                }
            }
            .navigationBarTitle("", displayMode: .inline)
            .navigationBarBackButtonHidden(true)
        }
    }
}
struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
struct ContentView2: View {
    
    var body: some View {
        
            ZStack {
                Color.black
                    .edgesIgnoringSafeArea([.all])
                NavigationLink(destination: ContentView()) {
                    Text("push")
                }
            }
            .navigationBarTitle("My Custom White title", displayMode: .inline)
        
    }
}

P. S: The code is taken from here

Eldar
  • 458
  • 3
  • 13
0

If you want to avoid affecting the appearance of all UINavigationBars when modifying UINavigationBar.appearance(), consider the following code:

import SwiftUI

struct NavigationStyleLayer: UIViewControllerRepresentable {
  @MainActor
  final class ViewController: UIViewController {
    override func viewDidLoad() {
      super.viewDidLoad()
      view.backgroundColor = .clear
      view.isUserInteractionEnabled = false
    }

    override func didMove(toParent parent: UIViewController?) {
      super.didMove(toParent: parent)

      if let navigationController = parent?.navigationController as? UINavigationController {
        navigationController.navigationBar.standardAppearance.largeTitleTextAttributes = [.font: UIFont.systemFont(ofSize: 17.0)]
      }
    }
  }

  func makeUIViewController(context: Context) -> ViewController {
    .init()
  }

  func updateUIViewController(_ uiViewController: ViewController, context: Context) {

  }
}

In your SwiftUI View code...

import SwiftUI

struct SwiftUIView: View {
  var body: some View {
    NavigationStack {
      Text("Hello, World!")
        .navigationTitle("Hello World!")
        .background {
          NavigationStyleLayer()
        }
    }
  }
}

Jinwoo Kim
  • 440
  • 4
  • 7
0

Can both change large and small titles. It can be better with more default init, but it's enough for me.

init() {
        NavigationBarConfigurator.configureTitles()
            }
struct NavigationBarConfigurator {
    static func configureTitles() {
        let appearance = UINavigationBarAppearance()       
        let design = UIFontDescriptor.SystemDesign.rounded
        if let descriptorWithDesign = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .largeTitle)
                                                     .withDesign(design),
           let descriptorWithTraits = descriptorWithDesign.withSymbolicTraits(.traitBold) {
            let font = UIFont(descriptor: descriptorWithTraits, size: 34)
            appearance.largeTitleTextAttributes = [.font: font, .foregroundColor: UIColor.label]
        }
        if let smallTitleDescriptorWithDesign = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .headline)                                                              .withDesign(design) {
                    let smallTitleFont = UIFont(descriptor: smallTitleDescriptorWithDesign, size: 24)
            appearance.titleTextAttributes = [.font:smallTitleFont, .foregroundColor: UIColor.label]
                }
        UINavigationBar.appearance().standardAppearance = appearance
    }
}