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.
Related
I have tested this on a sample project with 2 view controllers defined in the storyboard using Xcode 11 (iOS 13). The "presenting" view controller is embedded in a navigation controller and has the navigation bar colors set in the viewWillAppear.
The "search" view controller adds a UISearchController in the viewDidLoad and is pushed by the presenting view controller (NOT modal).
With just this setup when the search view controller is displayed the navigation bar has the blue background and red tint color as expected. However when scrolling down and the search bar is displayed the background color of the navigation bar is lost (or changed to what appears to be the default iOS grey / translucent). However if you scroll back up (hide the search bar) or focus on the search bar text field the navigation bar color returns!
Also if you focus on the search bar text field and then cancel (by tapping the Cancel button) the tint color of the navigation bar reverts from red to the default iOS blue as can be noticed by the back bar item.
Any suggestions on resolving this issue?
I have set the navigation bar colors in the viewWillAppear of the search controller too which didn't change this behaviour.
I set isTranslucent to true for the navigation bar in the search controller which seemed to prevent the reverting of the background color but it did not change the reverting of the tint color on cancel.
Presenting View Controller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.barTintColor = .blue
navigationController?.navigationBar.tintColor = .red
}
Search View Controller
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Search VC"
searchController.dimsBackgroundDuringPresentation = false
searchController.obscuresBackgroundDuringPresentation = false
navigationItem.searchController = searchController
definesPresentationContext = true
}
EDIT
Setting the scrollEdgeAppearance, backButtonAppearance and buttonAppearance as suggested works a treat except for system bar buttons that default to the iOS blue. This can be resolved by setting the UINavigationBar.tintColor but neither that resolves the back button chevron color defaulting on cancel of the search.
if #available(iOS 13.0, *) {
let buttonAppearance = UIBarButtonItemAppearance()
buttonAppearance.configureWithDefault(for: .plain)
buttonAppearance.normal.titleTextAttributes = [.foregroundColor: UIColor.red]
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithOpaqueBackground()
navigationBarAppearance.backgroundColor = .blue
navigationBarAppearance.titleTextAttributes = [.foregroundColor: UIColor.red]
navigationBarAppearance.backButtonAppearance = buttonAppearance
navigationBarAppearance.buttonAppearance = buttonAppearance
navigationBarAppearance.doneButtonAppearance = buttonAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navigationBarAppearance
navigationController?.navigationBar.compactAppearance = navigationBarAppearance
navigationController?.navigationBar.standardAppearance = navigationBarAppearance
} else {
navigationController?.navigationBar.barTintColor = .blue
navigationController?.navigationBar.titleTextAttributes = [.foregroundColor: UIColor.red]
navigationController?.navigationBar.tintColor = .red
}
However when scrolling down and the search bar is displayed the background color of the navigation bar is lost
All of that is normal. New in iOS 13, the expanded nav bar (displaying search bar, large title, etc.) has different appearance from the normal nav bar. Your settings applied only to the normal nav bar because you didn't make them the iOS 13 way. If you want the expanded nav bar to look like the normal nav bar, you have to set its appearance separately and explicitly.
To do so, you need to set the navigation bar's scrollEdgeAppearance. Investigate classes UIBarAppearance, UINavigationBarAppearance, and UIBarButtonItemAppearance (you will need to set the backButtonAppearance explicitly).
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.
How is it possible to customize the navigation bar like in the AppStore app?
I already tried to set the following (it is a subclass of UINavigationController):
self.navigationBar.setBackgroundImage(UIImage.with(color: UIColor.red), for: .default)
self.navigationBar.shadowImage = UIImage()
self.navigationBar.isTranslucent = true
self.view.backgroundColor = .blue
Then the large navigationBar is blue and the small/collapsed navigationBar is red. But what I have to adjust that I get a translucent navigationBar when the navigationBar is collapsed?
[
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
}
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)