Is there an easier way to set the PreferredStatusBarStyle of my app? - ios

I'm super unhappy with the way I've had to set the EFFING UIStatusBarStyle of my app for iOS 7. Essentially I've got a custom presenter that sets up the SlidingPanels navigation (hamburger menu). Inside the custom presenter I define a RootController, and this is where I'm confused/ticked off/annoyed... pick one. </rant>
Normally I would like to just do something like this and be done with it.
RootController = new UIViewController();
// this line won't work because PreferredStatusBarStyle is a Method Group and not a property WTF
RootController.PreferredStatusBarStyle = UIStatusBarStyle.LightContent;
But there seems to be no way to cleanly set properties in iOS. Therefore I'm stuck with this ugliness.
RootController = new CustomUiViewController();
//.....
public class CustomUiViewController : UIViewController
{
public override UIStatusBarStyle PreferredStatusBarStyle()
{
return UIStatusBarStyle.LightContent;
}
}
Then in the ViewDidLoad() of every view, I have to call SetNeedsStatusBarAppearanceUpdate(), and this is absurd to me.
Is there a cleaner/easier way to set this?
One of the side affects of the above approach is when the app is first loading, the StatusBar is still "dark" and therefore you can't see the clock until after ViewDidLoad().

Just add to your app's info.plist a couple keys:
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>

Related

Hiding everything in the iOS navbar, including icons

I've managed to hide the navbar itself, but I want it all gone - charge icon, connection bars, clock - absolutely everything.
I'm aware this may create a 'dead end' for my app, but that's ok for my purposes.
I read here that it's apparently not legal. Is that still true?
The app is not for distribution, so I'd still be interested to hear solutions even if Apple doesn't like it.
Thank you.
I assume you want to hide status bar.
You can use prefersStatusBarHidden property to hide status bar in Swift 4.2:
class ViewController: UIViewController {
override var prefersStatusBarHidden: Bool {
return hideStatusBar
}
override func viewDidLoad() {
super.viewDidLoad()
}
}

iOS: Default status bar style with UIViewControllerBasedStatusBarAppearance YES

Is there a way how to set the default status bar style while keeping the UIViewControllerBasedStatusBarAppearance enabled?
Here is the problem, I'm dealing with:
Nearly the whole app needs to be using UIStatusBarStyle.LightContent as the navigation bar has a dark background. Originally, UIViewControllerBasedStatusBarAppearance was disabled and the following was set in in Info.plist for while text status status bar:
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
This worked just fine until I found out that this .LightContent status bar is shown even for some of the share extensions, like Facebook Messenger, causing it to be unreadable:
This could be solved by using UIViewControllerBasedStatusBarAppearance, but then I would need to add the following method to all of view controllers which I want to avoid as the app is quite large.
Moreover, for the one screen in the app that has light nav bar background, I was switching to dark nav bar using UIApplication.sharedApplication().setStatusBarStyle() but this method in deprecated in iOS 9.
Any ideas how to solve this? Swizzling?
Solution
The easiest and cleanest way how to achieve that is to add the following line in AppDelegate's application:willFinishLaunchingWithOptions method:
UINavigationBar.appearance().barStyle = .Black
This will make .LightContent as the default status bar style thorough the app as long as your app uses UINavigationController.
Don't forget to keep the following setting in app's Info.plist if want to use .LightContent status bar style during launch for splash screen:
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
TL;DR
My current setup, which is very similar to many other apps, uses UITabBarController as the top most controller with UINavigationController stack for each tab.
UINavigationController takes care of the status bar style (as it should) and do not call preferredStatusBarStyle() on its child view controllers. Therefore, implementing the subclassing solution proposed by par does not work in my case.
Further subclassing the custom subclass of UINavigationController I'm using would not be a clean solution either.
Now, since the app has UIViewControllerBasedStatusBarAppearance enabled and correct status bar style everywhere in the app itself, SFSafariViewController and share extension like Messages, Mail, etc use the correct (.Default) status bar style too.
The only exception where the correct status bar style is not used is the Facebook Messenger's share extension mentioned in the question. However, it seems to be a bug in the extension itself as all apps I have tried that use .LightContent status bar style (like Twitter, for example) have the same issue - presented FB Messenger share extension from the app has a status bar with white color text.
A solution I use quite frequently is to create a base view controller class that all view controllers in my app derive from. This has the advantage of allowing use of the view-controller-based status bar style-setting functionality with a default (light or dark) style, which can then be overridden on a per-view-controller basis as necessary.
A base view controller is also really handy once you start getting into trait-collection based changes, custom transition animations that you want for most view controllers, a central point for analytics tracking, and other useful things.
Yes, you have to go through your potentially large source base and change all your UIViewControllers into BaseViewControllers, but this is often as easy as a global search-and-replace.
Here's what the BaseViewController looks like with status-bar related methods:
class BaseViewController: UIViewController {
var statusBarHidden: Bool = false { didSet { setNeedsStatusBarAppearanceUpdate() } }
var statusBarStyle: UIStatusBarStyle = .lightContent { didSet { setNeedsStatusBarAppearanceUpdate() } }
var statusBarUpdateAnimation: UIStatusBarAnimation = .fade { didSet { setNeedsStatusBarAppearanceUpdate() } }
override var preferredStatusBarStyle: UIStatusBarStyle { return statusBarStyle }
override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { return statusBarUpdateAnimation }
override var prefersStatusBarHidden: Bool { return statusBarHidden }
}
For all view controllers that use the default light style, you don't need to do anything special:
class ViewController: BaseViewController { }
In the cases where you need a dark status bar, do:
class DarkStatusBarViewController: BaseViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
statusBarStyle = .default
}
}
Note also that you could rename the DarkStatusBarViewController above to DarkStatusBarBaseViewController and derive from it instead of BaseViewController when you need a dark status bar. Then you don't need to duplicate the status bar code in every view controller that needs it and you maintain a nice linear relationship for all your BaseViewController functionality.

Set all back button titles in UINavigationController

I'm trying to be clever about setting all title properties of the the "Back" buttons in a UINavigationController so that I don't have to do self.navigationController.navigationBar.backButtonItem.title = "Back" everywhere or subclass a UINavigationController and set it everywhere, so I've created this extension:
extension UINavigationItem {
open var backBarButtonItem: UIBarButtonItem? {
get {
return self.backBarButtonItem
}
set {
newValue?.title = "Back"
backBarButtonItem = newValue?
}
}
}
But it says 'backBarButtonItem' used within its own type.
Has anybody done this before or can think of a way to make it work?
You are getting this error because you cannot create a variable with the name which is similar to those variables which are defined in the SDK.
You can't override the existing functionality
Like in your case you are naming it as backBarButtonTitle which is defined as open var backBarButtonItem: UIBarButtonItem? in UINavigationBar class of UIKit
As it is mentioned in doc of Apple
Extensions can add new functionality to a type, but they cannot
override existing functionality.
Please follow this Screen shot Image then run your project . I think you can solved your problem easily :)

UISplitViewController: How to prevent expansion when rotating from Compact to Regular

There are many answers to the complementary question, which is how to prevent a transition to PrimaryOverLay on a from Regular to Compact interface change, eg use
func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController: UIViewController, ontoPrimaryViewController primaryViewController: UIViewController) -> Bool
In my case, I have an iPhone 6+ with the detail view showing in portrait. When I rotate the device to horizontal (Compact to Regular), I want the primary view to stay hidden. I've tried setting the preferredDisplayMode to .PrimaryHidden in many places, but it has no apparent affect. Googling has turned up nothing.
Well, after I wrote the question, but before posting it, I tripped on a possible solution, which is to override the trait collection that the split view controller references.
I took that idea and decided to subclass UISplitViewController, and override the traitCollection property. That did the trick:
final class MySplitViewController: UISplitViewController {
var didOnce = false
override var traitCollection: UITraitCollection {
let old = super.traitCollection
let change = UITraitCollection(horizontalSizeClass: .Compact)
let new = UITraitCollection(traitsFromCollections: [old, change])
return new
}
Obviously this is hardcoded for one device - later I'll go and add some functions that I can use to control what is in fact returned.
Don't override traitCollection, instead use the method setOverrideTraitCollection:forChildViewController: in a parent view controller of your split controller, like in Apple's example AAPLTraitOverrideViewController.m
If your split controller doesn't have a parent, making a parent is really easy in the Storyboard. Add a new view controller, make it the entry point, add a container view, delete the default embedded view and instead add an embed segue to the split controller and set the override on self.childViewControllers.firstObject in viewDidLoad.

Restricting portrait orientation to collectionview in iOS doesn't seem to work

I have multiple viewControllers in my app and I would like to restrict certain views only to portrait orientation. I have achieved this by overriding shouldAutoRotate and supportedInterfaceOrientations like below
override func shouldAutorotate() -> Bool {
return false
}
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientation.Portrait.rawValue)
}
I have followed the same for a UICollectionView but it doesn't seem to work. I would like to know if this is the right way to achieve it. Help on this would be much appreciated. Thanks in advance!
In iOS 8 or later, if you want to control the allowed orientations per view controller:
Make sure the "Supported interface orientations" in the Info.plist allows all orientations you want the app to be able to use. (Or just check the correct "Device Orientations" in the Xcode UI interface to the Info.plistfile)
Per view controller, implement (example restricting to portrait):
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
A problem you can run in to is if your view controller is embedded in another view controller (like an UINavigationController or a UITabBarController, or both).
I suspect this is the situation you are describing.
If so, all view controllers involved need to implement the supportedInterfaceOrientations() method. AFAIK, if you for example have a UINavigationController above your view controller, you need to sub class and create your own navigation controller with the method implemented.

Resources