Unable to make SFSafariViewController statusbar style lightContent - ios

I have a requirement of light content in status bar with black background, however some of the screen needs black status bar content with white background, hence I've kept View controller-based status bar appearance to YES in info.plist to adopt status bar style based on view controllers requirement.
My problem is whenever I present SFSafariViewController from any view controller it is taking black status bar content and white background by default i.e status bar style is .default everytime.
I tried overriding preferredStatusBarStyle in SFSafariViewController subclass and no look so far.
Below is my code
import UIKit
import SafariServices
extension SFSafariViewController {
override open var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .lightContent
}
}
class MyViewController: UIViewController, SFSafariViewControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.barTintColor = UIColor.lightGray
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
#IBAction func presentSafari(sender: AnyObject) {
let safari = SFSafariViewController(url: URL(string: "https://www.google.com/")!)
safari.delegate = self
present(safari, animated: true) {
}
}
// MARK: - SFSafariViewControllerDelegate
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
dismiss(animated: true, completion: nil)
}
}

Set modalPresentationCapturesStatusBarAppearance to takes over control of status bar appearance from the presenting view controller.
#IBAction func presentSafari(sender: AnyObject) {
let safari = SFSafariViewController(url: URL(string: "https://www.google.com/")!)
safari.delegate = self
safari.modalPresentationCapturesStatusBarAppearance = true
if #available(iOS 10.0, *) {
safari.preferredBarTintColor = .yellow
} else {
// Fallback on earlier versions
safari.view.tintColor = .yellow
}
present(safari, animated: true) {
}
}
extension SFSafariViewController {
override open var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
}
When you present a view controller by calling the present(_:animated:completion:) method, status bar appearance control is transferred from the presenting to the presented view controller only if the presented controller's modalPresentationStyle value is UIModalPresentationStyle.fullScreen. By setting this property to true, you specify the presented view controller controls status bar appearance, even though presented non-fullscreen.
Output: Screenshot

iOS 10.0+
preferredBarTintColor
The color to tint the background of the navigation bar and the toolbar.
Ref: https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller/2274394-preferredbartintcolor
Since your View controller-based status bar appearance is set to YES in Info.plist, you will need to apply the color information on preferredBarTintColor, like so:
let safari = SFSafariViewController(url: URL(string: "https://google.com")!)
//This:
safari.preferredBarTintColor = .black
present(safari, animated: true, completion: nil)
And... no need for the following:
extension SFSafariViewController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return .default
}
}

Related

Changing Image Picker Preferred Status Bar Style swift

the status bar style for my application is white except when image picker controller is presented and I have already extend my UINavigationController but it doesn't seem to be working on any view present only on pushed views does anyone have solution??
extension UINavigationController {
open override var preferredStatusBarStyle: UIStatusBarStyle {
return topViewController?.preferredStatusBarStyle ?? .lightContent
}
}
I have also try this method , but the navigationController is a let and the
preferredStatusBarStyle is read-only
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
viewController.navigationItem.title = "willShow"
navigationController.preferredStatusBarStyle = UIStatusBarStyle.lightContent
}
When you present something modally and you want it to determine the status bar style you need to set modalPresentationCapturesStatusBarAppearance = true
For example:
let navigationController = UINavigationController(rootViewController: MyViewController())
navigationController.modalPresentationCapturesStatusBarAppearance = true
present(navigationController, animated: true)
You'll also need to check if the current UINavigationController is a UIImagePickerController and return .lightContent from preferredStatusBarStyle as UIImagePickerController has a prefers the .default out of the box.
open override var preferredStatusBarStyle: UIStatusBarStyle {
if self is UIImagePickerController {
return .lightContent
}
return topViewController?.preferredStatusBarStyle ?? .lightContent
}

NavigationBar title just changes once (not white again then going back)

I created a new project. I have a NavigationController. In the RootViewController I have a containerView with a table and just one cell. If I click on the cell I push a new UIViewController. So my Main.storyboard looks like this:
What I want:
I want first to have a white NavigationBartitle. Then pushing to secondVC I want to change the NavigationBarTitle to black. Then clicking on back the color should change back to a white title.
What I've did:
I did a custom NavigationViewController. There I was changing the func willShow viewController. In this I wrote the titleColor should change depending on which screen the navigationController changes to.
My code:
import UIKit
class SettingsNavigationViewController: UINavigationController {}
// MARK: - Controller Lifecycle
extension SettingsNavigationViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override var preferredStatusBarStyle: UIStatusBarStyle {
guard let child = self.childViewControllers.last else {
return .lightContent
}
return child is ViewController ? .lightContent : .default
}
}
// MARK: - NavigationController Delegate Implementation
extension SettingsNavigationViewController: UINavigationControllerDelegate {
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
let isSettingsContainer = viewController is ViewController
let backgroundColor = isSettingsContainer ? UIColor.cyan : UIColor.white
let titleColor = isSettingsContainer ? UIColor.white : UIColor.black
let image = isSettingsContainer ? UIImage() : nil
navigationController.navigationBar.shadowImage = image
navigationController.navigationBar.setBackgroundImage(image, for: .default)
navigationController.transitionCoordinator?.animate(alongsideTransition: { (context) in
navigationController.navigationBar.tintColor = titleColor
navigationController.navigationBar.barTintColor = backgroundColor
navigationController.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor : titleColor]
})
}
}
What happened instead:
If I change the screen to seconndVC the navBarTitleColor changes black. If I click on back it stays black. But it should change to white.
The complete project I also uploaded to github: https://github.com/Sonius94/stackNaviTitle
The possible solution could be, add a SecondViewController and in your SecondViewController you should implement the following :
override func willMove(toParentViewController parent: UIViewController?) {
super.willMove(toParentViewController: parent)
if parent == nil {
// Add your navigation bar appearance for FirstViewController
}
}

set custom color of status bar in swift?

On a view controller, i want to set the color of status bar black but I am not able to change it. I am using below code for this purpose.
func setUpUI() {
self.navigationController?.setNavigationBarHidden(true, animated: false)
UIApplication.shared.statusBarStyle = UIStatusBarStyle.default
}
I have also added the value in info.plist
You just override the return value of the status bar, like this:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Use below code for swift 4 and Xcode 9.2.
1) In
info.plist Set
View controller-based status bar appearance Key to
NO
2) In
AppDelegate
didFinishLaunchingWithOptions
set Below Code
let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
if statusBar.responds(to:#selector(setter: UIView.backgroundColor)) {
statusBar.backgroundColor = UIColor.red
}
UIApplication.shared.statusBarStyle = .lightContent

Safari View Controller uses wrong status bar color

My app uses a dark navigation bar color. Therefore I set the status bar color to white (so it has a nice contrast).
I did this by setting the barStyle to black (to make the status bar white) and also setting the barTint to my dark red color. Works perfectly.
I present a SafariViewController like this:
func openWebsite(urlString: String) {
if let url = NSURL(string: urlString) {
let svc = SFSafariViewController(URL: url)
svc.delegate = self
self.presentViewController(svc, animated: true, completion: nil)
}
}
However the status bar of the presented SafariViewController still is white. This is a problem because the SVC navigation bar has the default white transparent iOS default style. So the status bar is basically invisible.
How can I fix that?
You can achieve that by wrapping SFSafariViewController with subclassed UINavigationController.
BlackStatusBarNavigationController.h
#interface BlackStatusBarNavigationController : UINavigationController
#end
BlackStatusBarNavigationController.h
#interface BlackStatusBarNavigationController ()
#end
#implementation BlackStatusBarNavigationController
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleDefault;
}
#end
Use like this:
UINavigationController *navigationController = [[BlackStatusBarNavigationController alloc] initWithRootViewController:viewController];
navigationController.navigationBarHidden = YES;
[self presentViewController:navigationController animated:YES completion:nil];
There are 2 ways you can override preferredStatusBarStyle in your viewControllers and return the one you want
- (UIStatusBarStyle)preferredStatusBarStyle
{
return UIStatusBarStyleLightContent;
}
override func preferredStatusBarStyle() -> UIStatusBarStyle {
return .Default
}
or you can set it manually with
[[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
UIApplication.sharedApplication().setStatusBarHidden(false, withAnimation: UIStatusBarAnimation.None)
how ever for setting it through sharedApplicaion you will need to add this in to your plist
"View controller-based status bar appearance" to NO
If you want to set background color of status bar for iOS 13+, you can try to set it force like this.
import UIKit
import SafariServices
class CustomSFSafariViewController: SFSafariViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
if #available(iOS 13.0, *) {
UIApplication.shared.statusBarView?.backgroundColor = .purple
}
setNeedsStatusBarAppearanceUpdate()
}
}
extension UIApplication {
#available(iOS 13.0, *)
var statusBarView: UIView? {
let tag = 3848245
let keyWindow = connectedScenes
.map({$0 as? UIWindowScene})
.compactMap({$0})
.first?.windows.first
if let statusBar = keyWindow?.viewWithTag(tag) {
return statusBar
} else {
let height = keyWindow?.windowScene?.statusBarManager?.statusBarFrame ?? .zero
let statusBarView = UIView(frame: height)
statusBarView.tag = tag
statusBarView.layer.zPosition = 999999
keyWindow?.addSubview(statusBarView)
return statusBarView
}
}
}
There are few useful properties for customization of SFSafariViewController which you can use:
preferredControlTintColor (color of toolbar items)
preferredBarTintColor (color of toolbar)
P.S. don't forget to use CustomSFSafariViewController instead of SFSafariViewController. You can do it like this.
let safariViewController = CustomSFSafariViewController(url: url)

how to change statusbar color in one view controller using swift?

UIApplication.sharedApplication().setStatusBarStyle(UIStatusBarStyle.LightContent, animated: true)
I use this one to change status bar to light in all app. But now I need to change it in just one View Controller back to black. How can I do that?
Set View controller-based status bar appearance in your project.plist to NO
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)
}
Xcode 8.1, Swift 3 Solution with #IBDesignable
This solution is a little bit different:
Subclass of UIViewController to centralize logic
No code for viewDidLoad or viewDidDisappear
Uses #IBDesignable so you can set your status bar color in the Attributes Inspector on the Storyboard
Step 1 - Setup Info.plist File
Step 2 - Subclass UIViewController
import UIKit
#IBDesignable
class DesignableViewController: UIViewController {
#IBInspectable var LightStatusBar: Bool = false
override var preferredStatusBarStyle: UIStatusBarStyle {
get {
if LightStatusBar {
return UIStatusBarStyle.lightContent
} else {
return UIStatusBarStyle.default
}
}
}
}
Step 3 - Inherit from DesignableViewController
Change the code for your ViewController(s) from:
class ViewController: UIViewController {
To:
class ViewController: DesignableViewController {
Step 4 - Set your preference on the Storyboard
Select the ViewControllers on the Storyboard and go to the Attributes Inspector:
Step 5 - Run project and test
In my project I setup a Tab Bar Controller with 2 View Controllers and switch back and forth between the two. Seems to work OK for me.
Solved:
Swift 3.1
Just using this code in View Controller:
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
Swift 3
Set View controller-based status bar appearance in your project.plist to NO
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UIApplication.shared.setStatusBarStyle(.default, animated: animated)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
UIApplication.shared.setStatusBarStyle(.lightContent, animated: animated)
}
An Objective-C answer:
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleDefault;
}
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
}
let color = UIColor(red:0.00, green:0.60, blue:0.48,alpha:1.0)
UINavigationBar.appearance().tintColor = UIColor.blue
UINavigationBar.appearance().barTintColor = color
OR
self.navigationController?.navigationBar.titleTextAttributes = [NSForegroundColorAttributeName: UIColor.white]
In swit4 this working fine in my project based on navigation bar
let app = UIApplication.shared
let statusBarHeight: CGFloat = app.statusBarFrame.size.height
let statusbarView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: statusBarHeight))
statusbarView.backgroundColor = UIColor.red
view.addSubview(statusbarView)
You can set status bar color using below code and its working for me
self.navigationController?.SetStatusBar(StatusBarbackgroundColor: ThemeBackColor, StatusTextColor: .black)
Thanks
Happy Coding :)
Swift 5,iOS 14,UIKit
Step 1: Add Status bar style
Step 2: Change in Info.Plist
Add View controller-based status bar appearance key with NO value
Step 3: In your base view controller add this extension
extension UIViewController
{
func setStatusBarColor(){
if #available(iOS 13, *)
{
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
let statusBarFrame = window?.windowScene?.statusBarManager?.statusBarFrame
let statusBar = UIView(frame: (statusBarFrame)!)
statusBar.backgroundColor = .orange
window?.addSubview(statusBar)
} else {
// ADD THE STATUS BAR AND SET A CUSTOM COLOR
let statusBar: UIView = UIApplication.shared.value(forKey: "statusBar") as! UIView
if statusBar.responds(to:#selector(setter: UIView.backgroundColor)) {
statusBar.backgroundColor = .orange
}
UIApplication.shared.statusBarStyle = .lightContent
}
}
}
Step 4: In your view controller use this extension
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setStatusBarColor()
}
If you have derived your view controllers from a common view controller then, you can simply do it like this:
Step 1:
Add this key to your app's info.plist file.
Step 2:
override this in common view controller (or a ParentViewController).
override var preferredStatusBarStyle: UIStatusBarStyle {
if self is YourChildViewController {
return .lightContent
}
return .default
}
That's it! No more fancy things.

Resources