Set the Navigation Bar Title Font
Asked Answered
E

8

50

This is a SwiftUI question, not UIKit

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?

Encephalomyelitis answered 14/6, 2019 at 3:8 Comment(0)
D
61

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)
        }
    }
}
Decrescendo answered 23/8, 2019 at 18:14 Comment(1)
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.Ban
T
22

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)
                }
            }
        }
}
Tied answered 15/7, 2022 at 18:43 Comment(1)
That only works with inline title. OP's screenshot clearly shows a large title.Vestpocket
P
13

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]
Porphyrin answered 20/11, 2019 at 21:31 Comment(4)
How to give fontWeight = .bold ?? Is is possible?Decrescendo
@AnjaliKevadiya may be you can create font descriptor with func withFamily(_ newFamily: String) -> UIFontDescriptor but you need a concrete string of rounded system bold font. I don't know it.Porphyrin
@AnjaliKevadiya on the descriptor, you can add .withSymbolicTraits(UIFontDescriptor.SymbolicTraits.traitBold)!Pediment
You can pass size: 0 to UIFont's initializer to use the default font size for .largeTitle.Vestpocket
A
7

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

}
Amphistylar answered 6/4, 2022 at 6:37 Comment(2)
and what's the purpose of the @Environment(\.theme)?Toronto
@AdamRóżyński That was a mistake. It was in my implementation but should not have been in this minimal example. I have removed it.Amphistylar
A
3

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

Amphibolous answered 8/4, 2021 at 17:16 Comment(0)
A
2

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

Arillode answered 16/5, 2023 at 7:37 Comment(0)
W
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
    }
}
Way answered 21/8, 2023 at 10:31 Comment(0)
A
-1

Inspired by @Anjali Kevadiya's answer, I found it best to switch to smaller, centered navigation bar titles like you see in subscreens of iPhone Settings, using:

.navigationBarTitle(Text("Dashboard").font(.subheadline), displayMode:.inline)
.navigationBarItems(trailing: Button(action: { ... }) { Text("Done") } )
Arlina answered 29/2 at 0:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.