5

(Xcode 6, Swift, iOS8)

I am trying to find the device orientation for iPad, and it seems to be needlessly complicated. where am I going wrong?

At first, I tried to use the interfaceOrientation of my UIViewController, but it is unreliable and deprecated. Apple recommend using the statusBarOrientation property. (1)

statusBarOrientation is of type UIApplication. I tried to create an instance like so:

var o: UIApplication

and then test:

if (o.statusBarOrientation.isLandscape) { ... }

but received error

Variable 'o' used before being initialized.

Which makes no sense to me at all. Why would I initialize it to a value? I would overwrite the value that I want!?

Then I tried simply creating a variable as recommended in the docs:

var statusBarOrientation: UIInterfaceOrientation

but on a trace:

statusBarOrientation = (UIKit.UIInterfaceOrientation) (0xa0)

So I tried to subclass UIApplication, and access the property through my subclass.

From here, more complexity. It appears that "Every app must have exactly one instance of UIApplication (or a subclass of UIApplication)." (2)

This led me to the following post, which seems to create a Main routine in Swift?!
Subclass UIApplication with Swift

Again, my goal is to grab the current device orientation, and test it in a conditional. Pseudocoded:

var o = (get device orientation)
if (o == portrait ) { ... } else { ... }

As you can see, I'm in the weeds... any help would be greatly appreciated.

EDIT: I did manage to get it sort-of working with the following:

var o = UIApplication.sharedApplication().statusBarOrientation;
if (o.isLandscape) { ...

however, on initial load, when the device is rotated to landscape, o.isLandscape is being reported as false.

  1. Search documentation for "UIViewController," and look in the "Configuring the View Rotation Settings." Under Discussion, "Do not use this property for informing layout decisions. Instead, use the statusBarOrientation property, described in UIApplication Class Reference."

  2. Search documentation for "UIApplication" and look under Subclassing notes

Community
  • 1
  • 1
kmiklas
  • 13,085
  • 22
  • 67
  • 103

2 Answers2

10

There appear to be a couple of things wrong here that I'll try to clear up. First of all, the following doesn't create an instance of UIApplication, merely a variable of that type:

var o: UIApplication

If you want to create an instance of UIApplication, you should be doing this:

let o: UIApplication = UIApplication()

But, as you discovered, this won't work for UIApplication; you shouldn't be creating additional instances of UIApplication. Instead, you should be accessing the singleton instance, sharedApplication():

let o: UIApplication = UIApplication.sharedApplication()

Note: You had the same issue with your orientation variable as you did with the app delegate variable. The following line doesn't say what value to put into variable statusBarOrientation; it is not initialized:

var statusBarOrientation: UIInterfaceOrientation

Any one of these lines does what you intended:

var statusBarOrientation: UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation
var o: UIInterfaceOrientation = UIApplication.sharedApplication().statusBarOrientation
let orientation = UIApplication.sharedApplication().statusBarOrientation

Anyway, when you put it all together, you can get the current status bar orientation with the following.

if let theAppDelegate = UIApplication.sharedApplication() {
    let orientation = theAppDelegate.statusBarOrientation

    if orientation.isPortrait {
        // Portrait
    } else {
        // Landscape
    }
}

ALSO

You need to specify that your app can support the landscape orientations. This can either be done via the orientation check boxes in the application summary, or programmatically through the view controller's supported orientations method (you can find plenty on this online).

ToolmakerSteve
  • 18,547
  • 14
  • 94
  • 196
Mick MacCallum
  • 129,200
  • 40
  • 280
  • 281
  • 1. This is not working on initial load. I am calling this in my viewDidLoad() routine for a view controller inside a navigation controller. 2. Any reason, on the fourth line, why I can't use orientation.isPortrait or orientation.isLandscape? tyvm :) – kmiklas Jun 19 '14 at 17:39
  • @kmiklas I'm not sure why that would be happening, and I'm unable to reproduce it. It works completely fine on my end. And sorry, no that way is fine. I guess I'm just having a hard time breaking some old habits. – Mick MacCallum Jun 19 '14 at 17:44
  • 1
    @kmiklas Actually, have you configured support for either of the landscape orientations? – Mick MacCallum Jun 19 '14 at 18:19
  • No, I have not... I didn't know that I had to. **ALSO** Per your note, "You have the same issue..." what is the core piece of knowledge that I'm missing? What do I have to study? tyvm :) – kmiklas Jun 19 '14 at 18:46
  • 1
    @kmiklas You need to specify that your app can support the landscape orientations. This can either be done via the orientation check boxes in the application summary, or programmatically through the view controller's supported orientations method (you can find plenty on this online). Anyway, the core problem is that you're creating variables, without giving them a value. When you write `var o: UIApplication` you're specifying that this variable should be called `o` and have the `UIApplication` type, but it's never give a value. In `var o: UIApplication = UIApplication()` o is a newly created.. – Mick MacCallum Jun 19 '14 at 19:23
  • ... instance of UIApplication. So I would suggest re-reading the classes section of Apple's book on Swift, just to sure up any additional doubts on how this works. – Mick MacCallum Jun 19 '14 at 19:24
  • @`0x7fffffff: Actually, have you configured support for either of the landscape orientations? I had not, but after I did, the App is correctly loading in either view. tyvm again. – kmiklas Jun 19 '14 at 20:23
  • FWIW, I've discovered a case where this does not give the desired result. If I switch from a fragment A that forces landscape orientation (to view an info grid that fits better that way), to a fragment B that allows either portrait or landscape orientation, BUT the phone has already been rotated to portrait before the fragment B appears, the status bar is (correctly) landscape from A, yet B's viewWillTransitionToSize is never called (that is where I was determining whether view was taller or wider). The status bar stays "stuck" in landscape, even though the phone is in portrait. – ToolmakerSteve Jan 24 '17 at 22:09
  • ... or it may have been a problem with other logic of mine, that forced the orientation. Just wanted to caution people that if they have their own code that alters orientation, then `statusBarOrientation` may be less useful. – ToolmakerSteve Jan 24 '17 at 22:34
6

This works with Xcode 6.1 to avoid the "Bound value in a conditional binding must be of Optional type UIApplication" error that occurs with the use of an if let statement.

let orientation = UIApplication.sharedApplication().statusBarOrientation

    if orientation.isPortrait {
        // Portrait
    } else {
        // Landscape
    }
amurray4
  • 125
  • 1
  • 7