What Is the "Right" Way to Handle Orientation Changes in iOS 8

What is the right way to handle orientation changes in iOS 8?

Apple recommends using size classes as a coarse measure of how much screen space is available, so that your UI can significantly change its layout and appearance. Consider that an iPad in portrait has the same size classes as it does in landscape (Regular width, Regular height). This means that your UI should be more or less similar between the two orientations.

However, the change from portrait to landscape in an iPad is significant enough that you may need to make some smaller adjustments to the UI, even though the size classes have not changed. Since the interface orientation related methods on UIViewController have been deprecated, Apple now recommends implementing the following new method in UIViewController as a replacement:

- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];

// Code here will execute before the rotation begins.
// Equivalent to placing it in the deprecated method -[willRotateToInterfaceOrientation:duration:]

[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {

// Place code here to perform animations during the rotation.
// You can pass nil or leave this block empty if not necessary.

} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {

// Code here will execute after the rotation has finished.
// Equivalent to placing it in the deprecated method -[didRotateFromInterfaceOrientation:]

}];
}

Great! Now you're getting callbacks right before the rotation starts, and after it finishes. But what about actually knowing whether the rotation is to portrait or to landscape?

Apple recommends thinking about rotation as simply a change in size of the parent view. In other words, during an iPad rotation from portrait to landscape, you can think of it as the root-level view simply changing its bounds.size from {768, 1024} to {1024, 768}. Knowing this then, you should use the size passed into the viewWillTransitionToSize:withTransitionCoordinator: method above to figure out whether you are rotating to portrait or landscape.

If you want an even more seamless way to migrate legacy code to the new iOS 8 way of doing things, consider using this simple category on UIView, which can be used to determine whether a view is "portrait" or "landscape" based on its size.

To recap:

  1. You should use size classes to determine when to show fundamentally different UIs (e.g. an "iPhone-like" UI vs. an "iPad-like" UI)
  2. If you need to make smaller adjustments to your UI when size classes don't change but your container (parent view) size does, such as when an iPad rotates, use the viewWillTransitionToSize:withTransitionCoordinator: callback in UIViewController.
  3. Every view in your app should only make layout decisions based on the space that it has been given to layout in. Let the natural hierarchy of views cascade this information down.
  4. Similarly, don't use the statusBarOrientation -- which is basically a device-level property -- to determine whether to layout a view for "portrait" vs "landscape". The status bar orientation should only be used by code dealing with things like UIWindow which actually live at the very root level of the app.

How to detect Orientation Change in Custom Keyboard Extension in iOS 8?

In order to update your custom keyboard when the orientation changes, override viewDidLayoutSubviews in the UIInputViewController. As far as I can tell, when a rotation occurs this method is always called.

Additionally, as the traditional [UIApplication sharedApplication] statusBarOrientation] doesn't work, to determine the current orientation use the following snippet:

if([UIScreen mainScreen].bounds.size.width < [UIScreen mainScreen].bounds.size.height){
//Keyboard is in Portrait
}
else{
//Keyboard is in Landscape
}

Hopefully this helps!

How do we dictate app orientation in iOS 8?

The answer to part one (launching into landscape) is unchanged from iOS 7: everything depends on the order of possible orientations in the Info.plist file. So, let's say that View Controller 1 says this:

-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}

Then we will coherently launch into landscape if a landscape orientation comes first in the Info.plist's supported interface orientations:

<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationPortrait</string>
</array>

One noteworthy change in iOS 8 is that by default the status bar is hidden when we're in landscape. But you can prevent this, if desired, with the appropriate override:

-(BOOL)prefersStatusBarHidden {
return NO;
}

That doesn't answer the second part of my question, which is how to force rotation when a view controller is presented. As I have explained in this answer, my sense is that this will become impossible in iOS 8. Your view controller and your views are expected to "adapt" — and so are you.

EDIT: It looks like in seed 4 the ability to force app rotation on view controller presentation/dismissal is returning!

How to detect orientation change?

Here's how I got it working:

In AppDelegate.swift inside the didFinishLaunchingWithOptions function I put:

NotificationCenter.default.addObserver(self, selector: #selector(AppDelegate.rotated), name: UIDevice.orientationDidChangeNotification, object: nil)

and then inside the AppDelegate class I put the following function:

func rotated() {
if UIDeviceOrientationIsLandscape(UIDevice.current.orientation) {
print("Landscape")
}

if UIDeviceOrientationIsPortrait(UIDevice.current.orientation) {
print("Portrait")
}
}

Hope this helps anyone else!

Thanks!

Swift - How to detect orientation changes


let const = "Background" //image name
let const2 = "GreyBackground" // image name
@IBOutlet weak var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()

imageView.image = UIImage(named: const)
// Do any additional setup after loading the view.
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
if UIDevice.current.orientation.isLandscape {
print("Landscape")
imageView.image = UIImage(named: const2)
} else {
print("Portrait")
imageView.image = UIImage(named: const)
}
}

iOS 8 - Launching the application in Landscape mode

The issue seems to be the order of calls when you set up the window. You need to call makeKeyAndVisible before you assign the rootViewController. The following works:

self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[self.window makeKeyAndVisible];
self.window.rootViewController = self.myMainViewController;

But if you change the order to:

self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.window.rootViewController = self.myMainViewController;
[self.window makeKeyAndVisible];

You get the behavior you are experiencing.

iOS 8 custom keyboards orientation change

One xib for both portrait and landscape

It's possible to have only one xib for both portrait and landscape, if you enable autolayout on your views in your xib file. These are the steps I did in my own test keyboard.

  1. Enable and setup autolayout on the views in the xib file
  2. Load and add that subview to the "inputView"
  3. Programmatically setup the constraints for this subview in relation to inputView

"inputView" automatically changes sizes when switching between portrait and landscape, so when you have constraints setup for your view in relation to "inputView", your view will adjust automatically.

Separate xib for portrait and landscape

If you do decide to have separate xib files for your portrait and landscape views, you might need to load and add the correct subview to inputView whenever the orientation changes. I haven't done this personally but I believe updateViewConstraints() is called whenever the orientation changes so you can implement your logic there.



Related Topics



Leave a reply



Submit