Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Large Navigation Bar Text With Multiple Colors

iOS 11 introduces the option for larger text in the Navigation Bar. I would like to have a title that uses multiple colors. For example:

enter image description here

It's fairly easy to set the title, and even to change the color of the entire title:

[[self navigationItem] setTitle: @"Colors"];
[[[self navigationController] navigationBar] setLargeTitleTextAttributes: @{NSForegroundColorAttributeName: [UIColor colorFromHex: redColor]}];

What I can't figure out is how to change just part of the title. For example, a way to select the range like this – NSRangeMake(0, 1) – so that I could apply a color to it.

This must be possible, right?

like image 297
Axeva Avatar asked Nov 13 '17 00:11

Axeva


People also ask

How do I change the navigation title text color?

Changing the text color The text color of the navigation bar can be changed using two inbuilt classes: navbar-light: This class will set the color of the text to dark. This is used when using a light background color. navbar-dark: This class will set the color of the text to light.

What is Scrolledgeappearance?

The appearance settings for the navigation bar when the edge of scrollable content aligns with the edge of the navigation bar. iOS 13.0+ iPadOS 13.0+ Mac Catalyst 13.1+ tvOS 13.0+

How do I change the text color in a title in Swift?

Go to Attributes inspector of Navigation Controller > Navigation Bar and set the desired color in Title Color menu. Save this answer.


2 Answers

There is no public API to set your own attributed text for the large title.

The solution is to navigate down the view hierarchy. You specifically mentioned you wanted to avoid this, but it's the only way to modify the colors while getting the rest of the UINavigationBar behavior for free.

Of course, you can always create your own UILabel and set its attributedText, but you will have to re-create any navigation bar animations and other behavior yourself.

Honestly the simplest solution is to modify your design so it doesn't require a multi-colored large title, as this is currently not supported.


I took a dive down the "spelunking" path, and there are a variety of visual issues with animations snapping back to the original text color.

Here is the code I used if it's useful for anyone trying to achieve a similar effect:

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    applyColorStyle(toLabels: findTitleLabels())
}

private func applyColorStyle(toLabels labels: [UILabel]) {
    for titleLabel in labels {
        let attributedString = NSMutableAttributedString(string: titleLabel.text ?? "")
        let fullRange = NSRange(location: 0, length: attributedString.length)
        attributedString.addAttribute(NSAttributedStringKey.font, value: titleLabel.font, range: fullRange)
        let colors = [UIColor.red, UIColor.orange, UIColor.yellow, UIColor.green, UIColor.blue, UIColor.purple]
        for (index, color) in colors.enumerated() {
            attributedString.addAttribute(NSAttributedStringKey.foregroundColor, value: color, range: NSRange(location: index, length: 1))
        }
        titleLabel.attributedText = attributedString
    }
}

private func findTitleLabels() -> [UILabel] {
    guard let navigationController = navigationController else { return [] }
    var labels = [UILabel]()
    for view in navigationController.navigationBar.subviews {
        for subview in view.subviews {
            if let label = subview as? UILabel {
                if label.text == title { labels.append(label) }
            }
        }
    }
    return labels
}

The downside of the "spelunking" approach is it's not a supported API, meaning that it could easily break in a future update or not work as intended in various edge cases.

like image 170
nathangitter Avatar answered Oct 22 '22 17:10

nathangitter


That's no easy feat unfortunately, it's not officially supported. Here's a few ways I'd consider:

• Override layoutSubiews() in a navigation bar subclass. Traverse the view hierarchy, and mess with the UILabel to apply attributes. I wouldn't recommend this, I've moved away from doing something similar - the iOS 11 navigation bar has a seemingly complex and unpredictable behavior. Things like interactive gestures don't always play nice with whatever you tack on. Your case is a little more simple than mine was though, so there's a chance this may work fine.

• Create your own navigation bar style view from scratch - one that doesn't inherit from UINavigationBar. This is the route I ended up taking when doing something similar. It's more work, and won't mirror future changes Apple make to the visual style - but on the other hand, your app will look the same even across older iOS versions, and you have complete control.

• Hide the nav bar separator, revert it to a non-large size, and place your own view underneath it mimicking the look (the background is a visual effect view with a .extraLight blur style) - you can place a custom label inside this view.

like image 22
Jordan Smith Avatar answered Oct 22 '22 15:10

Jordan Smith