Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Restoring Auto-Rotate after Forcing Orientation in Swift

In my iPhone app I have a view that I want to show only in portrait mode. When navigating to that view it should be automatically displayed in portrait view. When navigating away, the orientation should change back to what it was, or, if the device orientation has changed, adapt to that. I could find information on forcing an orientation and preventing auto-rotate. I could not find anything on how to change back to the correct orientation after navigating away from that view.

So my idea was to

  1. save the initial orientation (store in currentOrientation)
  2. subscribe to orientation change event to keep track of orientation changes while the content is locked to portrait (update currentOrientation)
  3. when leaving the view, restore the correct orientation using the currentOrientation value.

Edit (code now removed): Apart from it not working it was a dangerous way to go as it made extensive use of unsupported APIs.


Edit:

I believe this question can now be boiled down to the following:

  1. Is there a documented, supported way to force the interface orientation independent of the device orientation? setValue(UIInterfaceOrientation.Portrait.rawValue, forKey: "orientation") has been recommended many times on SO and elsewhere but it does indeed seem to be an unsupported hack.

  2. Is there a documented, supported way to update the interface orientation to the device orientation? That would be needed to "recover" from the forced interface orientation in another view without having to trigger auto rotation by turning the device back and forth.

Supported are supportedInterfaceOrientations() and shouldAutorotate(). But these will only lock the interfaceOrientation after the device has been turned to that position. They do not prevent wrong initial orientation.

There are many questions similar to this one, showing that this problem setting is not uncommon, but so far no satisfactory and complete solution using supported methods.

like image 697
jerry Avatar asked Jul 02 '15 21:07

jerry


1 Answers

I had a similar problem except I needed one view controller to only work in Landscape mode and another when it was in portrait. The way I achieved this was making a custom 'root' view controller. Then on the viewWillTransitionToSize method for that controller checking for orientation and non animatedly pushing the correct view controller (so it looks like a rotation to the user). And then in Interface Builder I set the view controller's orientation property explicitly instead of being inferred. You could apply this solution by having only the landscape orientation set on the restricted view controller and then on the portrait rotation doing nothing and disabling auto rotation on the restricted view controller.

Update

I haven't had the time to test any of these but these are just the ideas I used when implementing my solution for a different VC for a different orientation, some combination of the following should hopefully work I can't be a 100% certain about it cause I did this some months ago and don't exactly remember what did and didn't work.

First of all make sure that you have setup the constraints as shown in the screenshot. Mine has iPad full screen and landscape because that's what I was doing change yours to whatever you need (portrait and the size can be inferred).

View Controller Interface Builder

Now before doing anything else I would first check to see if this solved the problem. I needed the root view controller cause I needed a different VC for portrait and and a different one for landscape. You only need to restrict it so if this works than that's perfect otherwise there are a few other things you can try as mentioned below.

Once that's setup I would first go to the view controller who you want to restrict's class and prevent autorotation using:

 override func shouldAutorotate() -> Bool {
    return false
}

Now if you do that since you are restricting to portrait I'm guessing you don't really care about upside down so you don't need to do anything additional. If you do want to use the viewWillTransitionToSize method and rotate manually.

If things still don't work you can finally try the root controller way (but I would use this in the last case). Heres a sketch of it:

class VC : UIViewController { 
      override func viewDidLoad () { 
           UIDevice.currentDevice().beginGeneratingDeviceOrientationNotifications()
           NSNotificationCenter.defaultCenter().addObserver(self, selector: "orientationChanged:", name: "UIDeviceOrientationDidChangeNotification", object: nil)
         // this gives you access to notifications about rotations
      }
      func orientationChanged(sender: NSNotification)
      {
              // Here check the orientation using this:
              if UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) { // Landscape }
              if UIInterfaceOrientationIsPortrait(UIApplication.sharedApplication().statusBarOrientation) { // Portrait }
           // Now once only allow the portrait one to go in that conditional part of the view. If you're using a navigation controller push the vc otherwise just use presentViewController:animated:
      }
}

I used the different paths for the if statements to push the one I wanted accordingly but you can do just push the portrait one manually for both and hopefully one of the ways above will help you.

like image 199
Manav Gabhawala Avatar answered Oct 17 '22 15:10

Manav Gabhawala