How to fix wrong status bar orientation in iOS

Issue #280

Original post https://medium.com/fantageek/how-to-fix-wrong-status-bar-orientation-in-ios-f044f840b9ed


When I first started iOS, it was iOS 8 at that time, I had a bug that took my nearly a day to figure out. The issue was that the status bar always orients to device orientation despite I already locked my main ViewController to portrait. This was why I started notes project on GitHub detailing what issues I ‘ve been facing.
ViewController is locked to portrait but the status bar rotates when device rotates · Issue #2 ·…
PROBLEM The rootViewController is locked to portrait. When I rotates, the view controller is portrait, but the status…github.com

Now it is iOS 12, and to my surprise people still have this issues. Today I will revise this issue in iOS 12 project and use Swift.

Supported interface orientations

Most apps only support portrait mode, with 1 or 2 screens being in landscape for image viewing or video player. So we usually declare Portrait, Landscape Left and Landscape Right.

Since iOS 7, apps have simple design with focus on content. UIViewController was given bigger role in appearance specification.

Supposed that we have a MainViewController as the rootViewController and we want this to be locked in portrait mode.

[@UIApplicationMain](http://twitter.com/UIApplicationMain)
class AppDelegate: UIResponder, UIApplicationDelegate {
 var window: UIWindow?
 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
   window = UIWindow(frame: UIScreen.main.bounds)
   window?.rootViewController = MainViewController()
   window?.makeKeyAndVisible()
   return true
 }
}

There is a property called UIViewControllerBasedStatusBarAppearance that we can specify in Info.plist to assert that we want UIViewController to dynamically control status bar appearance.

A Boolean value indicating whether the status bar appearance is based on the style preferred for the current view controller.

Start by declaring in Info.plist

<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>

And in MainViewController , lock status bar to portrait

import UIKit

class MainViewController: UIViewController {

let label = UILabel()

override func viewDidLoad() {
    super.viewDidLoad()

view.backgroundColor = .yellow

label.textColor = .red
    label.text = "MainViewController"
    label.font = UIFont.preferredFont(forTextStyle: .headline)
    label.sizeToFit()

view.addSubview(label)
  }

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

label.center = CGPoint(x: view.bounds.size.width/2, y: view.bounds.size.height/2)
  }

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
    return .portrait
  }
}

The idea is that no matter orientation the device is, the status bar always stays in portrait mode, the same as our MainViewController .

But to my surprise, this is not the case !!! As you can see in the screenshot below, the MainViewController is in portrait, but the status bar is not.

Stop mixing code and Storyboard

It took me a while to figure out that while I declare UIWindow in code, I also have MainStoryboard that configures UIWindow

<key>UIMainStoryboardFile</key>
<string>Main</string>

And this UIWindow from Storyboard has a default root ViewController

import UIKit

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()

  }
}

The meaning of UIViewControllerBasedStatusBarAppearance

In iOS, the status bar is hidden in landscape by default.

  • Setting UIViewControllerBasedStatusBarAppearance to false means that we want this default behaviour

  • Setting UIViewControllerBasedStatusBarAppearance to true means we want to have control over the status bar appearance. So to hide the status bar, we must override prefersStatusBarHidden in the rootViewController, the presented view controller or the full screen view controller

As you can see, the problem is that the ViewController from UIWindow in Storyboard clashes with our MainViewController in code, hence a lot of confusion.

The solution is simple, remove Main.storyboard and don’t use that in project

<key>UIMainStoryboardFile</key>
<string>Main</string>

Now we get our desired behaviour, status bar and view controllers are always locked to portrait regardless of the device orientation.

If you use code like me to setup UI, remember to clean up the generated storyboard and ViewController that comes default when project was generated by Xcode.

Comments