How to debug navigationcontroller.tintColor not changing? - ios

For some reason I can't figure out why I'm not able to change the tintColor of my navigation bar buttons in my app and I don't know how to figure out why. Can you recommend how I can debug this issue?
I am adding this to the viewDidLoad of my view controller:
UINavigationBar.appearance().tintColor = .green
However, nothing changes and the back button and Aa bar button stay the default grey color.
The navigation + status bars are translucent and sit on top of the background color of the viewcontroller through out the app.
I am able to change the barbutton to .green in a test app where I do the exact same (add it to ViewDidLoad of a vc, make sure that the barbutton images assets are set to Template so they'll take on the color I set). But for whatever reason, that same thing won't change in my app prototype.
Are there are any common reasons for this that I might not know of?
Added: the bar button was added via storyboard only.
And here is the code attempt to change its color via the viewDidLoad of 1 viewcontroller:
Class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
UINavigationBar.appearance().tintColor = .green
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
}
}

If you want to use the global appearance() proxy, then you can't do it in your view controller.
If you want to change the tintColor in viewDidLoad of your view controller then you have to set it on your controller's navigationController instance directly:
navigationController?.navigationBar.tintColor = .green
To use the appearance() proxy, and set a global tintColor for navigation bars, you'll have to do it before your navigation controller is initialized.
For example, it will work if you do it in your app delegate's application(_:didFinishLaunchingWithOptions:):
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
UINavigationBar.appearance().tintColor = .green
return true
}
See the UIAppearance documentation for details of why this is:
https://developer.apple.com/documentation/uikit/uiappearance
"iOS applies appearance changes when a view enters a window, it doesn’t change the appearance of a view that’s already in a window. To change the appearance of a view that’s currently in a window, remove the view from the view hierarchy and then put it back."

Navigation Bar color
To change the background color of the navigation bar, use the below code
navigationController?.navigationBar.barTintColor = .green
Change .green with your desired color.
Navigation Bar Text Color
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.green]
Change .green with your desired color.

UINavigationBar.appearance().barTintColor = UIColor(hex:"YourColorcode")
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white,
NSFontAttributeName: UIFont(name:"AvenirNext-Medium", size:19.0)]
UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.white,
NSFontAttributeName: "YourFontName"], for: UIControlState.normal)

Related

UITabBar barStyle change - inconsistent behavior on iOS 13

Let me explain the setup. The appdelegate creates a navigation controller and sets my ViewController as its rootViewController. Then a tabbarcontroller is created and this navigationcontroller is added to it. iOS 13 darkmode is ignored/opted out of by setting overrideUserInterfaceStyle to light on tabbar object.
The viewController has a button. When pressing this button, it switches between dark mode and light mode. It changes the barStyle of navigationBar and tabBar to either dark or default depending on the isDark property. When I first tap the button to change the theme, the navigationBar changes style but the tabBar doesn't. However, you do any kind of UI update, like pulling up the app switcher, activating control center, changing background color, animating something etc, the tabBar instantly changes to what its style was set. This only happens on iOS 13 (I made sure traitColletion isn't changing). The same thing works perfectly on iOS 12 and below. If you update any content that's under the tabBar, it updates the style to what you set it to.
Here's the code on my viewController
class TestViewController: UIViewController
{
override var preferredStatusBarStyle: UIStatusBarStyle
{
return .default
}
var dark = true
override func viewDidLoad()
{
title = "ABCD"
view.backgroundColor = .white
super.viewDidLoad()
let button = UIButton()
button.setTitleColor(.systemTintBlue, for: .normal)
button.setTitle("Theme", for: .normal)
button.addTarget(self, action: #selector(buttonPress), for: .touchUpInside)
view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
#objc func buttonPress()
{
if dark
{
// view.backgroundColor = .gray
tabBarController?.tabBar.barStyle = .black
navigationController?.navigationBar.barStyle = .black
dark = false
return
}
dark = true
tabBarController?.tabBar.barStyle = .default
navigationController?.navigationBar.barStyle = .default
// view.backgroundColor = .darkGray
}
}
Other observations
if you subclass UITabBarController and apply a certain style in viewDidLoad, updating the style later won't work, you'd then have to change UITabBarAppearance and apply a blur effect yourself. Or, you can set the style in viewDidAppear
If you subclass UITabBarController, override overrideUserInterfaceStyle to light, and change barStyle to default in viewDidLoad, the tabbar appears with a white blur and can be toggled between dark and light as you wish. But if you set dark barStyle, it'll appear with dark blur and can't be changed later
If you subclass UITabBarController, override overrideUserInterfaceStyle to dark, no matter what you set the barStyle to in viewDidLoad, the barStyle will always be dark and can't be changed
None of these stupid issues happens on iOS 12 or blow. Or this is a simulator bug. I don't have a physical iOS 13 device.

"PrefersLargeTitles" preventing background color change in all navigation bars

I have a tableViewController as my root view controller.
I've been trying to change the color of the status bar to match something like this:
However, when I set:
navigationController?.navigationBar.prefersLargeTitles = true
And apply:
override func viewDidAppear(_ animated: Bool) {
navigationController?.navigationBar.barTintColor = UIColor.blue
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key(rawValue: NSAttributedString.Key.foregroundColor.rawValue): UIColor.blue]
}
The background color doesn't change at all.
Only when I begin SCROLLING and the navigation bar collapses, do I get a color.
Has anyone else ran into this issue before? I built another viewController to test out "PrefersLargeTitles" WITHOUT a tableview scroll feature. And there was no background color either.

Navigation bar becomes white when a UISearchController is added to it

When I add a UISearchController to a UINavigationItem from an UINavigationController; it becomes white when the view loads and changes to the color specified when the user clicks on the search bar. This happened since ios 13.1. This video shows the behaviour:
https://imgur.com/wn5zbnJ
My code consists of a simple storyboard with a NavigationController + a TableViewController, and the NavigationController has a color assigned to it:
The ViewController consists of the following code:
class ViewController: UITableViewController {
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
searchController.hidesNavigationBarDuringPresentation = false
searchController.obscuresBackgroundDuringPresentation = false
navigationItem.searchController = searchController
}
}
I also added these keys to the info.plist file to force the app into light-mode, but if I remove these the same behaviour is still present:
<key>UIUserInterfaceStyle</key>
<string>Light</string>
This was tested on an iPhone XS Max, running iOS 13.1 beta 1. Is this expected behaviour or a bug which needs to be fixed?
It looks like it is required to use the new UINavigationBarAppearance on iOS 13. Try to add this to your viewDidLoad:
let appearance = UINavigationBarAppearance()
appearance.backgroundColor = .systemRed
appearance.titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
navigationItem.standardAppearance = appearance
navigationItem.scrollEdgeAppearance = appearance
You will probably also want to set the searchField backgroundColor:
let searchField = searchController.searchBar.searchTextField
searchField.backgroundColor = .systemBackground
This appears to be a bug in iOS 13.1. Specifically, there is a new iOS 13 specific appearance (UINavigationBarAppearance) for navigation bars which specifies the appearance when the scroll view is scrolled to the top, along with the default state. Normally changes like this only go into effect when the app is built with the corresponding SDK (iOS 13.1). However, there seems to be a bug where the behavior also occurs when an app is built using the iOS 12 SDK.
See: https://developer.apple.com/documentation/uikit/uinavigationbarappearance
Update:
There is a workaround here: https://itnext.io/fixing-issues-caused-by-future-sdks-ae0896384abf
Essentially, if your app is running on a device running iOS 13, it's possible to create instances of the new classes via NSClassFromString() in swift, then use a bit of objective-c runtime magic to configure the navigation bar.
What you want was not clear in your question. If you want, however, add searchbar into navigationBar and with a specific color this might help you.
The process of placing seachbar into navigation bar:
let searchController = UISearchController(searchResultsController: nil)
navigationItem.searchController = searchController
You can add whatever controller you want into 'searchResultsController' value.
If you want to set background color into a specific color, you can change the bar tint from Storyborad -> navigationbar -> navigation bar attiribute inspection.
Also, this below code for the AppDelegate.swift file will do the same. 'tintcolor' and 'titletextcolor' is commented
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
let navigationBarAppearace = UINavigationBar.appearance()
navigationBarAppearace.barTintColor = UIColor.blue
// navigationBarAppearace.tintColor = UIColor.red
//navigationBarAppearace.titleTextAttributes = [.foregroundColor: UIColor.red]
return true
}
I'm experiencing same issue, and already opened a radar to Apple.
Even though, if you set the backgroundColor to the appearance of the UINavigationBar, then the navigation bar instead of becoming transparent when pulling down and showing the search bar becomes that color, but then the status bar still remains white.
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().barTintColor = .blue
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor : UIColor.white]
UINavigationBar.appearance().backgroundColor = .red
Here is some code that tries to mitigate that behaviour, but I'm open to hear for the status bar thing

How to change NavigationBarColor while app running

I use this to set NavigationBarColor before it run:
UINavigationBar.appearance().barTintColor = Color.NavigationBackground
But in the program,I want to change the NavigationBarColor, So I use this again
UINavigationBar.appearance().barTintColor = Color.Black
But nothing happen, It still white(Color.Background)
Color is a struct that I defined.
How to change the color correctly?
I want to achieve like this:Trying to reload view controller to update the current theme
if you want each screen to have different color add below line with color of your choice in view will appear and it will change color for each screen.
Swift 4.2:
//To change Navigation Bar Background Color
UINavigationBar.appearance().barTintColor = UIColor.blue
//To change Back button title & icon color
UINavigationBar.appearance().tintColor = UIColor.white
//To change Navigation Bar Title Color
UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white]
add it in view will appear and then you can see it changing.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.barTintColor = .orange
}
if you want to change the color of navigation bar color
navigationController?.navigationBar.barTintColor = UIColor.black
Use the appearance API, and barTintColor color.
UINavigationBar.appearance().barTintColor = UIColor.red
In your ViewController's viewWillAppear(_:) simply set navigationBar's barTintColor as your required color, i.e.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.barTintColor = .red
}
Use this code to set UINavigationBar color initially in your Appdelegate’s didFinishLaunchingWithOptions.
UINavigationBar.appearance().isTranslucent = false
UINavigationBar.appearance().backgroundColor = Color.Background
UINavigationBar.appearance().barTintColor = Color.Background
UINavigationBar.appearance().tintColor = UIColor.white
And when you want to change color of UINavigationBar within app, just use these lines of code. Let’s say change color is your button action.
#IBAction func changeThemeColor(_ sender: UIButton) {
self.navigationController?.navigationBar.backgroundColor = Color.Black
self.navigationController?.navigationBar.barTintColor = Color.Black
}

Does changing the appearance of the Navigation Bar in App Delegate override the embedded navigation bar?

I have 2 view controllers embedded in a navigation bar controller. Plus each view controller has a table view that covers the entire view.
The issue I am experiencing is that my back button (this is the left bar button item) in the 2nd view controller is not visible though I can tap on the top left corner of the screen and it takes me back to the previous view controller. Thus, the button is present just not visible.
I do have this code in my App Delegate because I need to change the appearance of the header title of the Navigation Bar. Is this overriding my Nav Bar in some way?
-AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//change navigation bar appearance
//--background color
UINavigationBar.appearance().barTintColor = UIColor.white
UINavigationBar.appearance().tintColor = UIColor.white
UINavigationBar.appearance().titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
UINavigationBar.appearance().isTranslucent = false
//--title color
let fontColor = UIColor(red: 80/255.0, green: 80/255.0, blue: 80/255.0, alpha: 1.0)
let font = UIFont(name: "Verdana-Bold", size: 20)!
let attributes: [String: AnyObject] = [
NSFontAttributeName: font,
NSForegroundColorAttributeName: fontColor
]
UINavigationBar.appearance().titleTextAttributes = attributes
return true
}
I did verify in the Attribute Inspector in my Navigation Controller Scene that the property 'Shows Navigation Bar' is checked.
I even tried to add a refresh button programmatically to the 1rst view controller like so and I can see the print statement in the console meaning that when I press the top left corner the console prints "Refresh Button Pressed" but again the button is not visible even though it functions.
-FirstViewController.swift
// MARK: - Target Action functions
#IBAction func refreshAction(_ sender: UIButton) {
print("Refresh Button Pressed")
//let _ = self.navigationController?.popViewController(animated: true)
}
func setNavigationBarAttributes() {
//set title for navigation bar (the appearance was changed in App Delegate)
self.navigationItem.title = "NYC Schools"
//refresh button
let refreshButton = UIButton(type: .custom)
refreshButton.setImage(UIImage(named: "back_button.png"), for: .normal) // Image can be downloaded from here below link
refreshButton.setTitle("Refresh", for: .normal)
refreshButton.setTitleColor(refreshButton.tintColor, for: .normal) // You can change the TitleColor
refreshButton.addTarget(self, action: #selector(self.refreshAction(_:)), for: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: refreshButton)
}
I think the issue with your invisible, working back button in your 2nd view controller is the fact that you have set the navigation bar barTintColor to white, and also the tintColor to white. This gives you a white back button on a white navigation bar - making it invisible!
Try it again with these settings in your AppDelegate:
UINavigationBar.appearance().barTintColor = UIColor.white
UINavigationBar.appearance().tintColor = UIColor.blue
As for your refresh button on the 1st view controller - I think there's two possibilities for why you can't see it:
"back_button.png" is a white or transparent image, which is not visible against the white navigation bar.
the "back_button.png" image you specified does not exist and you're creating a button with no image at all, which (I think) would render it invisible.

Resources