How to set multi line Large title in navigation bar? ( New feature of iOS 11) How to set multi line Large title in navigation bar? ( New feature of iOS 11) swift swift

How to set multi line Large title in navigation bar? ( New feature of iOS 11)


Based in @krunal answer, this is working for me:

extension UIViewController {func setupNavigationMultilineTitle() {    guard let navigationBar = self.navigationController?.navigationBar else { return }    for sview in navigationBar.subviews {        for ssview in sview.subviews {            guard let label = ssview as? UILabel else { break }            if label.text == self.title {                label.numberOfLines = 0                label.lineBreakMode = .byWordWrapping                label.sizeToFit()                UIView.animate(withDuration: 0.3, animations: {                    navigationBar.frame.size.height = 57 + label.frame.height                })            }        }    }}

In the UIViewController:

override func viewDidLoad() {    super.viewDidLoad()    self.title = "This is a multiline title"    setupNavigationMultilineTitle()}override func viewDidAppear(_ animated: Bool) {    super.viewDidAppear(animated)    setupNavigationMultilineTitle()}

And for setting font and color on the large title:

navigation.navigationBar.largeTitleTextAttributes = [NSAttributedStringKey.foregroundColor: .red, NSAttributedStringKey.font: UIFont.boldSystemFont(ofSize: 30)]


Get a navigation item subviews and locate UILabel from it.

Try this and see:

self.navigationController?.navigationBar.prefersLargeTitles = trueself.navigationController?.navigationItem.largeTitleDisplayMode = .automaticself.title = "This is multiline title for navigation bar"self.navigationController?.navigationBar.largeTitleTextAttributes = [                                                     NSAttributedStringKey.foregroundColor: UIColor.black,                                NSAttributedStringKey.font : UIFont.preferredFont(forTextStyle: .largeTitle)                                ]for navItem in(self.navigationController?.navigationBar.subviews)! {     for itemSubView in navItem.subviews {          if let largeLabel = itemSubView as? UILabel {             largeLabel.text = self.title             largeLabel.numberOfLines = 0             largeLabel.lineBreakMode = .byWordWrapping         }     }}

Here is result:

enter image description here


The linebreak solution seems to be problematic when there's a back button. So instead of breaking lines, I made the label auto adjust font.

func setupLargeTitleAutoAdjustFont() {    guard let navigationBar = navigationController?.navigationBar else {        return    }    // recursively find the label    func findLabel(in view: UIView) -> UILabel? {        if view.subviews.count > 0 {            for subview in view.subviews {                if let label = findLabel(in: subview) {                    return label                }            }        }        return view as? UILabel    }    if let label = findLabel(in: navigationBar) {        if label.text == self.title {            label.adjustsFontSizeToFitWidth = true            label.minimumScaleFactor = 0.7        }    }}

Then it needs to be called in viewDidLayoutSubviews() to make sure the label can be found, and we only need to call it once:

private lazy var setupLargeTitleLabelOnce: Void = {[unowned self] in    if #available(iOS 11.0, *) {        self.setupLargeTitleAutoAdjustFont()    }}()override func viewDidLayoutSubviews() {    super.viewDidLayoutSubviews()    let _ = setupLargeTitleLabelOnce}

If there's any navigationController pop event back to this controller, we need to call it again in viewDidAppear(). I haven't found a better solution for this - there's a small glitch of label font changing when coming back from a pop event:

override func viewDidAppear(_ animated: Bool) {    super.viewDidAppear(animated)    if #available(iOS 11.0, *) {        setupLargeTitleAutoAdjustFont()    }}