In my app, the primary theme has blue navigation bars with white bar button items and titles.
I set the values for colors like this in the App Delegate.
UINavigationBar.appearance().isTranslucent = false
UINavigationBar.appearance().tintColor = .white
UINavigationBar.appearance().barTintColor = AppColors.blue
UINavigationBar.appearance().titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
But in some view controllers, I'm required to invert these styles (white navigation bars with blue buttons and titles).
So in those view controllers, I simply set the new values in the viewWillAppear method.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.tintColor = AppColors.blue
navigationController?.navigationBar.barTintColor = .white
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: AppColors.blue]
}
From one of these view controllers, I have to show a UIDocumentPickerViewController to pick a document file.
let documentPickerViewController = UIDocumentPickerViewController(documentTypes: [String(kUTTypePDF)], in: .import)
documentPickerViewController.delegate = self
present(documentPickerViewController, animated: true)
The user can preview it as well. I use the QuickLook framework to do that.
The problem is these view controllers, UIDocumentPickerViewController and QLPreviewController don't conform to the new navigation bar color values I set in the viewWillAppear method.
Master view controller has the blue navigation bar but the detail view's navigation bar is all white. Including the bar button items. So the buttons are not visible. It seems the values I set in the viewWillAppear method has no effect here. I commented out the appearance values from the App Delegate and the buttons show up in the default colors.
Same with the QLPreviewController view controller.
I tried having the presenting view controller conform to UINavigationControllerDelegate but that didn't work.
I also tried getting a reference to the UIDocumentPickerViewController's navigation controller like this documentPickerViewController.navigationController but it returns nil as well.
How do I apply colors to the UIDocumentPickerViewController without changing the appearance values?
I uploaded a demo project here.
Related
I am really struggling figuring out why there's a part on top of the navigation bar that has a different color than the navigation bar.
I have set the navigation like this:
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.backgroundColor = .primaryBlack
navigationController?.setNavigationBarHidden(false, animated: true)
And for some reason only one part is with that color but not everything. In some other parts that part is completely transparent making it look really odd.
I encountered this issue when ever my navigation bar is using large titles and push a view controller onto the navigation stack of the navigation controller, this gap seems to be at the top:
There seems to be some gap created for some reason and the large title navigation bar starts under the status bar.
The reason I understand as per the documentation is that the base view goes under the UINavigationBar and when this view is scrolled, the UINavigationBar needs to transform.
When a navigation controller contains a navigation bar and a scroll
view, part of the scroll view’s content appears underneath the
navigation bar. If the edge of the scrolled content reaches that bar,
UIKit applies the appearance settings in this property.
Simplest thing I do is to make the underlying view the same color as the navigation bar background in the view controller and keep the result of your code the same:
// Same as your code, only changed background to blue
navigationController?.navigationBar.tintColor = .white
navigationController?.navigationBar.backgroundColor = .blue
navigationController?.setNavigationBarHidden(false, animated: true)
// What I added
view.backgroundColor = .blue
This gives me:
Update using UINavigationBarAppearance
Can you try updating using UINavigationBarAppearance
// From your code, not sure of it's purpose
navigationController?.setNavigationBarHidden(false, animated: true)
let appearance = UINavigationBarAppearance()
// Color of the nav bar background
appearance.backgroundColor = .black // primary black for you
// Color for the titles
appearance.largeTitleTextAttributes
= [NSAttributedString.Key.foregroundColor : UIColor.white]
appearance.titleTextAttributes
= [NSAttributedString.Key.foregroundColor : UIColor.white]
navigationController?.navigationBar.standardAppearance = appearance
navigationController?.navigationBar.scrollEdgeAppearance = appearance
// Color for the back button
navigationController?.navigationBar.tintColor = .white
Result:
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).
How to add view above navigation bar?
I have a custom navigation controller and I want to present a view above nav bar (like on the screen), so it should be visible on other ViewControllers
Would be great if the solution will be on storyboard.
Tried to add on UIWindow did't help.
Swift 4.2, Xcode 10+
Okay, from what I can tell (via your comment reply, though it still isn't 100% clear), the best solution to your question would be to make the navigation bar transparent, such that you can see any navigationController-presented view controllers underneath it. For this, I'd suggest the following extension to UIViewController:
extension UIViewController {
func setupTransparentNavigationBarWithBlackText() {
setupTransparentNavigationBar()
//Status bar text and back(item) tint to black
self.navigationController?.navigationBar.barStyle = .default
self.navigationController?.navigationBar.tintColor = .black
}
func setupTransparentNavigationBarWithWhiteText() {
setupTransparentNavigationBar()
//Status bar text and back(item) tint to white
self.navigationController?.navigationBar.barStyle = .blackTranslucent
self.navigationController?.navigationBar.tintColor = .white
}
func setupTransparentNavigationBar() {
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.backgroundColor = .clear
self.navigationController?.navigationBar.isTranslucent = true
}
}
Using either of the first two methods in viewWillAppear of your UIViewController subclasses will let you make the navigation bar completely transparent with the statusBar text + wifi/battery indicators black or white as desired. From this, you can then display anything under the navigation bar by pinning your constraints to view.bounds.topAnchor. E.g. for a transparent navigation controller with white statusBar text:
class YourViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
setupTransparentNavigationBarWithWhiteText()
}
}
I have three controller. let's say Controller A, B and C in sequence.
navigation title color in controller A is red.
blue in controller B and green in controller C.
When i push these viewcontroller navigation title color change perfectly. But when coming back to B from C or A from B navigation tile becomes green and blue respectively(sould be blue and red).
What i did:
I have embedded navigation controller from interface builder in storyboard. To change the title color i used following code in viewWillAppear of every view controller. I placed all my code in main queue. also checked without not placing them in async block and results are same.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
Dispatch.main.async{
self.navigationItem.title = "viewcontroller2"
let textAttributes = [NSAttributedStringKey.foregroundColor: UIColor.blue]
self.navigationController?.navigationBar.titleTextAttributes = textAttributes
}
}
Note that: This problem occur when i use navigation back button to go back but while swipe to back, title color works as it should be. iOS version : 11.4.1. In iOS ver 10.3.3 this issue is not occurring.
Please use like,
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.blue]
self.navigationController?.navigationBar.layoutIfNeeded()
Is there anyway to keep the tab bar color exactly the same regardless of the color of the view in the view controllers? The bottom picture with dashboard selected is darker than the top one because the view.backGroundcolor = .lightGray in the view controller.
I tried setting the view.bottomAnchor equal to the view.safeAreaLayoutGuide.bottomAnchor, but even then if the view is set as light gray, the tab bar will be slightly darker than the view controllers that have a white background.
I also set the self.tabBar.barTintColor = .white
and self.tabBar.alpha = 1.0
It's not just a perception thing either as I checked the exact color in hexcode.
Relevant lines of code :
final class TabBarViewController: UITabBarController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBar.barTintColor = .white
self.tabBar.alpha = 1.0
}
}
Should this code go in the init instead that sets up the corresponding view controllers?
The color you are setting for the tab bar is only a tint. The only way to get absolute control over the color is to make a resizable UIImage of the desired color and set the tab bar's backgroundImage property.
https://developer.apple.com/documentation/uikit/uitabbar/1623469-backgroundimage