16

I am using navigation controller, and I've set to true its navigation bar's prefersLargeTitle property. Everything works fine, but when the text of my title becomes too big, it doesn't fit in space. Here how it looks:

enter image description here

Is it possible to somehow make the title (while the navigation bar's prefersLargeTitle property is set to true) dynamically adjust its font size, and if it is so, how to achieve that?

Tigran Iskandaryan
  • 1,371
  • 2
  • 14
  • 41

5 Answers5

22

All you need is:

UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).adjustsFontSizeToFitWidth = true

Working also with SwiftUI on iOS 15.

Sound Blaster
  • 4,778
  • 1
  • 24
  • 32
21

This is the workaround that I found

override func viewDidLoad() {
  super.viewDidLoad()

  title = yourTitle
  adjustLargeTitleSize()
}

extension UIViewController {
  func adjustLargeTitleSize() {
    guard let title = title, #available(iOS 11.0, *) else { return }

    let maxWidth = UIScreen.main.bounds.size.width - 60
    var fontSize = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize
    var width = title.size(withAttributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: fontSize)]).width

    while width > maxWidth {
      fontSize -= 1
      width = title.size(withAttributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: fontSize)]).width
    }

    navigationController?.navigationBar.largeTitleTextAttributes =
        [NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: fontSize)
    ]
  }
}
vicente.fava
  • 360
  • 3
  • 14
  • 4
    this works great ! the only change I'd make is to replace the last line with the following(Swift 3 syntax ahead): `if var titleAttributes = navigationController?.navigationBar.largeTitleTextAttributes { titleAttributes[NSFontAttributeName] = UIFont.boldSystemFont(ofSize: fontSize) navigationController?.navigationBar.largeTitleTextAttributes = titleAttributes }` This retains the title's original attributes whilst only modifying the font size. Finally.. one may need to use `guard let title = self.navigationItem.title` depending on how they've set it. – batman Aug 08 '18 at 04:55
  • 1
    This should be the accepted answer. Not sure why the other one is. With a few tweaks of code for swift 3+, this works great. – eResourcesInc May 30 '19 at 17:59
  • I would also recommend taking a max() comparison of the calculated font size vs. a given required minimum. For very long titles, the font keeps getting infinitely small to accommodate this code. So just before you pass fontSize to the navigation bar, do this to make sure that the minimum font size never gets less than 14 points: let minFontSize = max(fontSize, 14). Then, pass minFontSize to the navigation bar. – eResourcesInc May 30 '19 at 18:03
  • 1
    Bravo. Works like a charm. (Xcode 11.4.1 + iOS 13.3) – titusmagnus Apr 30 '20 at 03:53
3

This question is somewhat answered here: How to resize Title in a navigation bar dynamically.

self.title = "Your TiTle Text"
let tlabel = UILabel(frame: CGRectMake(0, 0, 200, 40))
tlabel.text = self.title
tlabel.textColor = UIColor.whiteColor()
tlabel.font = UIFont(name: "Helvetica-Bold", size: 30.0)
tlabel.backgroundColor = UIColor.clearColor()
tlabel.adjustsFontSizeToFitWidth = true
self.navigationItem.titleView = tlabel

That being said, this is slightly different, in that you have the prefersLargeTitle property set. Now, I am not sure whether the tlabel.adjustsFontSizeToFitWidth = true overrides the prefersLargeTitle property, but try it out and see if it works. There is also some additional information regarding navigation item large titles here: https://developer.apple.com/documentation/uikit/uinavigationitem/2909056-largetitledisplaymode. Hope this helps.

casperson
  • 146
  • 12
  • It just adds the label on top of the large title, so now basically I have two titles. So, I have to choose between the two types of titles. But itself it works. Thanks for the answer. – Tigran Iskandaryan Nov 06 '17 at 22:48
  • Yes, but in that case I don't have the animation of large titles – Tigran Iskandaryan Nov 07 '17 at 12:39
  • Same here, this add a new separate title on top over the ```Large Title``` – TheAlphaGhost Feb 22 '19 at 12:56
  • @TigranIskandaryan - many of the people reading this question are going to need a solution for the normal use of Large Titles. They will scroll up and down enough to trigger the two types of title display. – benc May 02 '21 at 07:34
0

Tested for iOS12 ~ iOS14

extension UINavigationController {

    func adjustFontSize(with title: String) {
        let insetToEdge: CGFloat = 16
        let maxWidth = navigationBar.bounds.width - insetToEdge - insetToEdge
        let largeTitleFont = UIFont.preferredFont(forTextStyle: .largeTitle)
        var fontSize = largeTitleFont.pointSize

        var largeTitleTextAttributes: [NSAttributedString.Key: Any] = [:]

        var largeTitleSize: CGSize
        if #available(iOS 13.0, *) {
            largeTitleSize = NSAttributedString(
                string: title,
                attributes: navigationBar.standardAppearance.largeTitleTextAttributes)
                .size()
        } else {
            largeTitleTextAttributes = [NSAttributedString.Key.font: largeTitleFont]
            largeTitleSize = NSAttributedString(
                string: title,
                attributes: largeTitleTextAttributes)
                .size()
        }
        guard largeTitleSize.width > maxWidth else { return }
        while largeTitleSize.width > maxWidth {
            fontSize -= 1
            if #available(iOS 13.0, *) {
                largeTitleTextAttributes = navigationBar.standardAppearance.largeTitleTextAttributes
            }
            largeTitleTextAttributes[NSAttributedString.Key.font] = UIFont.BO.font(
                ofSize: fontSize,
                weight: .semiBold)
            largeTitleSize = NSAttributedString(
                string: title,
                attributes: largeTitleTextAttributes)
                .size()
        }
        if #available(iOS 13.0, *) {
            navigationBar.standardAppearance.largeTitleTextAttributes = largeTitleTextAttributes
        } else {
            navigationBar.largeTitleTextAttributes = largeTitleTextAttributes
        }
    }

}

call from viewDidLoad()

andrew54068
  • 1,326
  • 14
  • 29
-1

Made an edit to @vicente.fava answer - this works great.

self.title = longTitle
self.navigationController?.navigationBar.prefersLargeTitles = true
adjustLargeTitleSize()


extension UIViewController {
func adjustLargeTitleSize() {
    guard let title = title, #available(iOS 11.0, *) else { return }

    let maxWidth = UIScreen.main.bounds.size.width - 60
    var fontSize = UIFont.preferredFont(forTextStyle: .largeTitle).pointSize
    var width = title.size(withAttributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: fontSize)]).width

    while width > maxWidth {
        fontSize -= 1
        width = title.size(withAttributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: fontSize)]).width
    }

    navigationController?.navigationBar.largeTitleTextAttributes =
        [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: fontSize)
    ]
}

}

Paresh Masani
  • 7,474
  • 12
  • 73
  • 139