Determining statusbar orientation - ios

I am wanting to detect where the status bar is on my current view in order to determine which segue to perform. I know the preferred way to do this is to determine the device orientation, but there are two problems with this approach for me:
1.) If the device is not angled far enough away from vertical or horizontal then there is no device orientation detected and the segue doesn't happen
2.) Under certain conditions I am going to "lock" the display orientation such that even though the physical device orientation is landscape the screen is going to be locked to portrait and I will want to perform the portrait segue I have created.
The problem is that the " [UIApplication sharedApplication].statusBarOrientation = " is not returning the actual orientation of the status bar. Is there not an easy way to detect this? Otherwise I am going to have to write a bunch of messy code to keep track of this.

Have you tried: [[UIDevice currentDevice] orientation]?
Note:
Depending on the design of your app it might be necessary to call [[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]. If you do that, make sure to call [[UIDevice currentDevice] endGeneratingDeviceOrientationNotifications] at some point.
If that's not sufficient for your purpose I guess you have to manually check the accelerometer. Take a look at: Handling Motion Events for more info on how that's done.

Related

Spurious rotation events while switching tasks in ios 11

My app is crashing when you return to it after switching away from it using the 4-finger task-switch gesture on an iPad.
When the user does the 4-finger gesture to switch away from my app, I see applicationWillResignActive: then applicationDidEnterBackground: in my app delegate, then (assuming we're currently in portrait orientation) my top-level view controller gets viewWillTransitionToSize:withTransitionCoordinator: with a size that indicates landscape layout (even though the device has not rotated). This of course results in a lot of resizing and rearranging of views for the new orientation. Immediately after that I get viewWillTransitionToSize:withTransitionCoordinator: with a size that indicates portrait (the original orientation of the device). And again, I go through all my layout again for the new (actually, original) orientation.
What I'm finding is that if I wait for all of this to complete, I can switch in and out of my app all day. If, on the other hand, I switch back to my app while this needless work is going on, I get a crash deep in some iOS transition code.
At this point I'm trying to understand why I get these stray rotation events. If I can avoid getting those, I can avoid getting into whatever situation is causing the crash. They don't seem necessary.
I can't think of a reason why it would be useful or correct to get viewWillTransitionToSize in the background, so just bounce out if you are in the background:
if UIApplication.shared.applicationState == .background {
return
}
Still, this feels like a bug, and in my opinion you should report it to Apple.
It turns out there isn't a way to prevent the rotation events from being reported to the app. I suspect that's an iOS bug. However, ignoring viewWillTransitionToSize:withTransitionCoordinator: when the application state is UIApplicationStateBackground and doing the same in the view's layoutSubviews (if present) allowed me to work around the problem.

Detect device orientation in Init function

I am detecting the device orientation with these lines of code:
UIDevice.currentDevice().orientation.isPortrait.boolValue
UIDevice.currentDevice().orientation.isLandscape.boolValue
And I detect check if it's an iPad with this code:
userInterfaceIdiom == .Pad
I used this code in commonInit() function which is called in the Init() function. When I execute my code on iPad the two first lines return both false which is not correct, one of them should be true. The third line is working fine.
If I use the code in other functions, such as supportedInterfaceOrientations(), it works fine. Do you know what could be the problem?
You are probably neglecting this documentation:
You also use the UIDevice instance to detect changes in the device’s
characteristics, such as physical orientation. You get the current
orientation using the orientation property or receive change
notifications by registering for the
UIDeviceOrientationDidChangeNotification notification. Before using
either of these techniques to get orientation data, you must enable
data delivery using the beginGeneratingDeviceOrientationNotifications
method. When you no longer need to track the device orientation, call
the endGeneratingDeviceOrientationNotifications method to disable the
delivery of notifications.
Also, this is a 3-D orientation. You probably don't want that. What is the actual value of simply the orientation property? See:
enum UIDeviceOrientation : Int {
case Unknown
case Portrait
case PortraitUpsideDown
case LandscapeLeft
case LandscapeRight
case FaceUp
case FaceDown }
Probably what you really want is here:
"interfaceOrientation" is deprecated in iOS 8, How to change this method Objective C
Sometimes TraitCollections doesn't fill all your design needs. For
those cases, Apple recommends to compare view's bounds :
if view.bounds.size.width > view.bounds.size.height {
// ... }

How can you change orientation for testing on XCTest for iOS?

I'm trying to test my iOS app using XCTestCase in different orientations. I need a way to programmatic way to change the orientation. I tried doing this in 2 methods, but both didn't change the orientation (orientation remained as UIInterfaceOrientationPortrait).
Attempt 1
[UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeLeft;`
Attempt 2
[[UIDevice currentDevice] setValue:[NSNumber numberWithInt:UIInterfaceOrientationLandscapeLeft] forKey:#"orientation"];`
When I read [[UIApplication sharedApplication] statusBarOrientation]
Is there another way to change the orientation for testing purposes?
Swift code:
XCUIDevice.shared.orientation = UIDeviceOrientation.portrait;
use below solution. Do it in setup method.
[[UIDevice currentDevice] setValue:
[NSNumber numberWithInteger: UIInterfaceOrientationPortrait]
forKey:#"orientation"];
its working for me.
This should work, I am changing the orientation randomly because I am running several tests, I hope this is useful
// Set a random device orientation
let orient = [UIDeviceOrientation.portrait , UIDeviceOrientation.portraitUpsideDown, UIDeviceOrientation.landscapeLeft, UIDeviceOrientation.landscapeRight]
XCUIDevice.shared().orientation = orient[Int(arc4random_uniform(UInt32(orient.count)))]
If you want to run device rotating tests within the XCTest environment, my currently best known strategy is:
In Simulator, make sure the Rotate Device Automatically option is set.
Create an XCTest Host application and make sure its info.plist Supported interface orientations section is fully removed (you can't do that in your production app as the appStore will reject it.)
In the host application delegate implement -application:supportedInterfaceOrientationsForWindow: and return UIInterfaceOrientationMaskAll. This will effectively replace the former info.plist entries.
Then within your tests, call the private [UIDevice.currentDevice setValue:#(UIDeviceOrientation…) forKey:#"orientation"]; at need
This animates, so wait until all windows have changed their orientation by checking their frames and then continue your testing.
To speed up animations in tests, set window.layer.speed = 100. This makes the animations 100 times faster and hopefully your tests too.

[UIDevice cuurentDevice].orientation returning previous values in appDidBecomeActive callback

If I take the app to background by pressing the home button, then change the device orientation and bring the app to foreground, I still get the previous device orientation value in [UIDevice currentDevice].orientation.
For example, I hold the device in Default orientation, take the app to background and rotate the device to UpsideDown orientation, then bring the app to foreground, the deviceOrientation is still Default on being queried. Only after some time, the listener for beginDeviceOrientationEvents gets called and correct value is available.
So I want to ask, where should be the first place to fetch the correct value of deviceOrientation on app relaunch?
Use [[UIDevice currentDevice] orientation] (as described in the docs) in your app delegate's applicationDidBecomeActive method.
Update
In fact, [[UIApplication sharedApplication] statusBarOrientation] might be more useful, as the value returned takes into account which orientations you have set your app to be compatible with.

iPad OpenGL Screen Rotation Problems (Multitasking bar and Popups not Rotating)

I'm building an application for the iPad using OpenGL and have issues with screen rotation. The application itself works fine and displays correctly in all four orientations, but other elements only partially respond to the device rotation.
OpenGL handles the rotation of the application using transformation matrices. That all works fine. The default images also display at the correct rotation.
The multitask bar, however, is not rotating as it should. It appears on the side that the application was opened in. So if I open the application then double tap the home button it is on the correct side, but after rotating the device it remains on the same side until the application is closed and reopened. When this bar is revealed the OpenGL application rotates to match the bar's position.
Dialogue boxes, such as the 'sign into game center' popup are also not correctly rotated. They seem to appear in the standard portrait orientation.
I'm not sure if there are any other specific details necessary to identify the problem but any suggestions are very much appreciated.
I seem to have put together a solution looking at snippets in some other threads. I think it's fixed the problem.
I have a main class which controls most of my application. It was already registered to receive device rotation notifications like this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
I added a few lines to the orientation handler function to manually set the status bar location:
- (void) updateOrientation {
if ([[UIDevice currentDevice] orientation] < 5 && [[UIDevice currentDevice] orientation]>0) {
orientation = [[UIDevice currentDevice] orientation];
[UIApplication sharedApplication].statusBarOrientation = orientation;
}
}
The conditional limits the handler to respond only to landscape and portrait orientations and not face up and face down.
If anybody has a better solution I'd still like to hear it, otherwise, hope this can help someone else.

Resources