Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't change UINavigationBar prompt color

I am unable to change the prompt color on my navigation bar. I've tried the code below in viewDidLoad, but nothing happens.

self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]

navigation bar image

Am I missing something? Is the code above wrong?

like image 615
Jake Avatar asked Sep 17 '17 16:09

Jake


3 Answers

I was able to make the prompt color white on iOS 11 was setting the barStyle to black. I set the other color attributes (like the desired background color) using the appearance proxy:

myNavbar.barStyle = UIBarStyleBlack; // Objective-C
myNavbar.barStyle = .black // Swift
like image 50
Markus Forsström Avatar answered Oct 24 '22 00:10

Markus Forsström


It seems like you're right about this one. You need to use UIAppearance to style the prompt text on iOS 11.

I've filed radar #34758558 that the titleTextAttributes property just stopped working for prompt in iOS 11.

The good news is that there are a couple of workarounds, which we can uncover by using Xcode's view hierarchy debugger:

Screenshot of UINavigation Bar Prompt on iOS 11

// 1. This works, but potentially changes *all* labels in the navigation bar. 
// If you want this, it works.
UILabel.appearance(whenContainedInInstancesOf: [UINavigationBar.self]).textColor = UIColor.white

The prompt is just a UILabel. If we use UIAppearance's whenContainedInInstancesOf:, we can pretty easily update the color the way we want.

If you look closely, you'll notice that there's also a wrapper view on the UILabel. It has its own class that might respond to UIAppearance...

Screenshot of private UINavigationBar subview

// 2. This is a more precise workaround but it requires using a private class.

if let promptClass = NSClassFromString("_UINavigationBarModernPromptView") as? UIAppearanceContainer.Type
{
  UILabel.appearance(whenContainedInInstancesOf: [promptClass]).textColor = UIColor.white
}

I'd advise sticking to the more general solution, since it doesn't use private API. (App review, etc.) Check out what you get with either of these two solutions:

Properly colored prompt text

like image 42
Moshe Avatar answered Oct 23 '22 23:10

Moshe


More complicated version to support old and new iOS

    func updatePromptUI(for state: State) {
    if (state != .Online) {
        //workaround for SOFT-7019 (iOS 11 bug - Offline message has transparent background)
        if #available(iOS 11.0, *) {
            showPromptView()
        } else {
            showOldPromptView()
        }
    }
    else {
        self.navigationItem.prompt = nil
        if #available(iOS 11.0, *) {
            self.removePromptView()
        } else {
            self.navigationController?.navigationBar.titleTextAttributes = nil
            self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor:UIColor.lightGray]
        }
    }
}

private func showOldPromptView() {
    self.navigationItem.prompt = "Network Offline. Some actions may be unavailable."
    let navbarFont = UIFont.systemFont(ofSize: 16)
    self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.font: navbarFont, NSAttributedStringKey.foregroundColor:UIColor.white]
}

private func showPromptView() {
    self.navigationItem.prompt = String()
    self.removePromptView()

    let promptView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 18))
    promptView.backgroundColor = .red
    let promptLabel = UILabel(frame: CGRect(x: 0, y: 2, width: promptView.frame.width, height: 14))
    promptLabel.text = "Network Offline. Some actions may be unavailable."
    promptLabel.textColor = .white
    promptLabel.textAlignment = .center
    promptLabel.font = promptLabel.font.withSize(13)
    promptView.addSubview(promptLabel)
    self.navigationController?.navigationBar.addSubview(promptView)
}

private func removePromptView() {
    for view in self.navigationController?.navigationBar.subviews ?? [] {
        view.removeFromSuperview()
    }
}
like image 3
Igor Lebedev Avatar answered Oct 23 '22 23:10

Igor Lebedev