UIDocumentPicker navigation bar buttons are hidden at iOS 11 - swift

I notice a problem in my UIDocumentPicker's navigation bar at iOS 11 only, the done, cancel, or edit buttons are invisible, and when the user touch it it appears i.e. The color at normal state is white, even when changing the UINavigationBar.appearnce().tintColor, The color only changed on touch.

For unknown reason I figured out that if you make a subclass of UIDocumentPicker using Objective-C and set the [UINavigationBar appearance].tintColor = [UIColor black]; in viewWillAppear func, and reset it to your defaults in the viewWillDisappear, it works well.
But if you do the same steps using swift it wont.

I'm not a big fan of setting the global appearance between viewWillAppear and viewWillDisappear. The appearance API should be used at application start only. You can just reset the appearance for UIDocumentPickerViewController only without subclassing by putting this code in application:didFinishLaunchingWithOptions: and the bar buttons will return the their original blue:
if #available(iOS 11.0, *) {
UINavigationBar.appearance(whenContainedInInstancesOf: [UIDocumentBrowserViewController.self]).tintColor = nil
}

Use CustomDocumentPickerViewController with black appearance for UINavigationBar and UIBarButtonItem
import UIKit
class CustomDocumentPickerViewController: UIDocumentPickerViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UINavigationBar.appearance().tintColor = UIColor.black
UIBarButtonItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName : UIColor.black], for: .normal)
}
override func viewWillDisappear(_ animated: Bool) {
UINavigationBar.appearance().tintColor = UIColor.white // your color
UIBarButtonItem.appearance().setTitleTextAttributes(nil, for: .normal)
super.viewWillDisappear(animated)
}
}

Related

SwiftUI - TabView with NavigationView generates gray area

I have some problems with my tabbed view when I set isTranslucent to false in combination with a NavigationView.
Does anyone know how to fix this? The problem is shown in the attached image.
I need translucent set to false otherwise I can't get the dark color.
You can set backgroundColor. Don't set isTranslucent to false or it will create these artefacts you mentioned.
UITabBar.appearance().backgroundColor = .black
UINavigationBar.appearance().backgroundColor = .black
It becomes much darker. It isn't completely opaque though.
Edit: Just watched Modernizing Your UI for iOS 13 This is the way to do it :
The TabView and NavigationView are actually UIHostedController for the legacy UITabBarController and UINavigationController:
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.titleTextAttributes = [.foregroundColor: UIColor.white]
appearance.largeTitleTextAttributes = [.foregroundColor: UIColor .white]
Then set the appearance on the various type of appearance.
tabBar.standardAppearance = appearance
2nd Edit:
extension UINavigationController {
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let appearance = UINavigationBarAppearance()
appearance.configureWithOpaqueBackground()
navigationBar.standardAppearance = appearance
navigationBar.compactAppearance = appearance
navigationBar.scrollEdgeAppearance = appearance
}
}
extension UITabBarController {
override open func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
tabBar.standardAppearance = appearance
}
}
There should be a cleaner way to get to both tabBar and navBar.
Reference: https://developer.apple.com/videos/play/wwdc2019/224/
I was using UIKit with SwiftUI. My Tab bar was creating in storyboard but the view for which I was getting extra bottom space was a swiftui view as you mentioned. I tried all above solution but Nothing worked for me.
I am using Xcode 12.4 for development. My solution is to mark Translucent to true in storyboard and Bottom extra gray bar was gone.
Just customize it in an extension like this:
extension UITabBarController {
override open func viewDidLoad() {
super.viewDidLoad()
let appearance = UITabBarAppearance()
appearance.backgroundColor = .black
tabBar.standardAppearance = appearance
}
}
Pay attention that the overridden function must be viewDidLoad(). At least it doesn't work for me when it is a viewDidAppear(:) function.
It's easier than all that, just delete the next line:
UITabBar.appearance().isTranslucent = false

How to change Navigation Bar back to translucent after making it transparent

I have a view controller in my navigation stack that needs to have a transparent navigation bar, while still showing the back button.
I'm able to achieve that with one line of code inside viewWillAppear:
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
However, when I try to go back to the previous view, I'm setting the background image back to nil or .none but I'm losing the translucent effect that was previously on there when I do that.
I've tried setting all the following options in viewWillDisappear and none seem to bring the translucency back. It just appears white no matter what I do. The shadow on the bottom is also gone too:
self.navigationController?.navigationBar.isTranslucent = true
self.navigationController?.navigationBar.barStyle = .default
self.navigationController?.navigationBar.backgroundColor = .none
self.navigationController?.navigationBar.setBackgroundImage(.none, for: .default)
Initial Navigation Bar:
Transparent Navigation Bar:
After Transitioning Back:
In viewWillAppear make the navigation bar transparent
override func viewWillAppear(_ animated: Bool) { self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
self.navigationController?.navigationBar.isTranslucent = true
}
And backg to translucent in viewWillDisappear
override func viewWillDisappear(_ animated: Bool) {
self.navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
self.navigationController?.navigationBar.shadowImage = nil
self.navigationController?.navigationBar.isTranslucent = false
}
After spending time poking around in the UINavigationBar internals, I did discover a simple method that seems to work, and does not require any configuration of the standard UINavigationBar attributes we've previously fiddled with to achieve transparency. The following is tested working on iOS 12.2.x:
class TallNavigationBar: UINavigationBar {
private lazy var maskingView: UIView = {
let view = UIView(frame: bounds)
view.backgroundColor = .clear
return view
}()
var isTransparent = false {
didSet {
guard isTransparent != oldValue, let bkgView = subviews.first else { return }
bkgView.mask = isTransparent ? maskingView : nil
}
}
}
Obviously, whenever fiddling (even slightly) with undocumented internals: use at your own risk!
This worked for my app which needs to revert to an opaque navigation bar after popping from a transparent navigation bar.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.setBackgroundImage(nil, for: .default)
navigationController?.navigationBar.shadowImage = nil
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.backgroundColor = nil
}

Strange behaviour with setting navigationBar.tintColor in viewDidLoad

I am experiencing an unexpected behaviour with setting UINavigationBar.tintColor in viewDidLoad().
I have two ViewControllers ViewController and SecondViewController embedded in a UINavigationController. A UIButton is triggering the segue from ViewController to SecondViewController. Both ViewControllers have set a UIBarButtonItem with a custom UIImage.
In my SecondViewController i want to have a different tintColor of my navigationbar.
This is my code:
import UIKit
class ViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.navigationBar.tintColor = nil
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.tintColor = UIColor(red: 100/255, green: 200/255, blue: 0/255, alpha: 1.0)
}
}
Storyboard looks like this:
When the transition from ViewController to SecondViewController is happening, the UIBarButtonItem on the right side set is jiggling a bit. I was expecting the item to change its color but not to jiggle in any way.
Does anyone have suggestions for this behaviour?
(I also tried to set the tintColor in viewWillAppear but this does not solve the problem. If i set the the tintColor in viewDidAppear everything is fine. But this is too late for my use case.)
If you need further informations give suggestions let me know i will provide any information needed.
GIF showing this problem:
Please try to change color in viewDidLayoutSubviews().
viewDidLayoutSubviews() Called to notify the view controller that its view has just laid out its subviews.
This is the small trick to solve this type of problem.
Learn more from Apple developer site: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621398-viewdidlayoutsubviews

Cannot set bartintcolor on iPhone 6 plus

So my problem is that I cannot set the bar tint color on an iPhone 6 plus. I can set the bar tint color for all other devices but for the iPhone 6 plus the bar tint won't change. Here is the code for the view controller. Additionally, this VC is being pushed onto the stack by a navigation controller. Any help is majorly appreciated all.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.barTintColor = .redColor()
navigationController?.navigationBar.translucent = false
navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.whiteColor()]
navigationController?.navigationBar.tintColor = UIColor.whiteColor()
}
This is the only thing that I am doing in the view controller and it doesn't work at all.
Ok so I solved the issue. For some reason, iPhone 6 plus calls
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
navigationController?.navigationBar.barTintColor = .primaryGrayColor()
navigationController?.navigationBar.translucent = false
navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.blackColor()]
}
every time a new view controller is pushed onto a navigation stack. This does not happen in the iPhone 5, iPhone 5s, iPhone 6 or the iPhone 7.

How do you change the text color in the statusbar in MFMailComposeViewController using Swift

I'm trying to change the text color in the status bar to match the rest of the apps appearance. I can change the text color in the nav bar for the MFMailComposeViewController easily enough:
myEmailComposerViewConotroller.navigationBar.tintColor = UIColor.whiteColor()
Am I missing something in the API on changing the text color of the status bar?
From my understanding of your question, I think you're going to want to do something like this:
myEmailComposerViewConotroller.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName : UIColor.whiteColor()]
titleTextAttributes is of type [NSObject: AnyObject]?
so that is why we initialize it to:
[NSForegroundColorAttributeName : UIColor.whiteColor()]
I already answered sth similar here: how to change statusbar color in one view controller using swift?
Set View controller-based status bar appearance in your project.plist to NO
Subclass the MFMailViewController and implement custom viewWillAppear and viewWillDisappear functions
Use viewWillAppear and will viewWillDisappear to set and reset the statusBarStyle, while keeping a property with the previous statusBarStyle like this
let initialStatusBarStyle : UIStatusBarStyle
func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
initialStatusBarStyle = UIApplication.sharedApplication().statusBarStyle
UIApplication.sharedApplication().setStatusBarStyle(.LightContent, animated: animated)
}
func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
UIApplication.sharedApplication().setStatusBarStyle(initialStatusBarStyle, animated: animated)
}