How to force view controller orientation in iOS 8?

2018-12-31 15:39发布

Before iOS 8, we used below code in conjunction with supportedInterfaceOrientations and shouldAutoRotate delegate methods to force app orientation to any particular orientation. I used below code snippet to programmatically rotate the app to desired orientation. Firstly, I am changing the status bar orientation. And then just presenting and immediately dismissing a modal view rotates the view to desired orientation.

[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:YES];
UIViewController *c = [[UIViewController alloc]init];
[self presentViewController:vc animated:NO completion:nil];
[self dismissViewControllerAnimated:NO completion:nil];

But this is failing in iOS 8. Also, I have seen some answers in stack overflow where people suggested that we should always avoid this approach from iOS 8 onwards.

To be more specific, my application is a universal type of application. There are three controllers in total.

  1. First View controller- It should support all orientations in iPad and only portrait (home button down) in iPhone.

  2. Second View controller- It should support only landscape right in all conditions

  3. Third View controller- It should support only landscape right in all conditions

We are using navigation controller for page navigation. From the first view controller, on a button click action, we are pushing the second one on stack. So, when the second view controller arrives, irrespective of device orientation, the app should lock in landscape right only.

Below is my shouldAutorotate and supportedInterfaceOrientations methods in second and third view controller.

-(NSUInteger)supportedInterfaceOrientations{
    return UIInterfaceOrientationMaskLandscapeRight;
}

-(BOOL)shouldAutorotate {
    return NO;
}

Is there any solution for this or any better way of locking a view controller in particular orientation for iOS 8. Please help!!

24条回答
初与友歌
2楼-- · 2018-12-31 16:08

This is what worked for me:

https://developer.apple.com/library//ios/documentation/UIKit/Reference/UIViewController_Class/index.html#//apple_ref/occ/clm/UIViewController/attemptRotationToDeviceOrientation

Call it in your viewDidAppear: method.

- (void) viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];

    [UIViewController attemptRotationToDeviceOrientation];
}
查看更多
宁负流年不负卿
3楼-- · 2018-12-31 16:09

The combination of Sids and Koreys answers worked for me.

Extending the Navigation Controller:

extension UINavigationController {
    public override func shouldAutorotate() -> Bool {
        return visibleViewController.shouldAutorotate()
    }
}

Then disabling rotation on the single View

class ViewController: UIViewController {
    override func shouldAutorotate() -> Bool {
        return false
    }
}

And rotating to the appropriate orientation before the segue

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "SomeSegue")
    {
        let value = UIInterfaceOrientation.Portrait.rawValue;
        UIDevice.currentDevice().setValue(value, forKey: "orientation")
    }
}
查看更多
孤独总比滥情好
4楼-- · 2018-12-31 16:10
- (void)viewDidAppear:(BOOL)animated
{
    [super viewDidAppear:animated];
    [UIViewController attemptRotationToDeviceOrientation];
}
查看更多
步步皆殇っ
5楼-- · 2018-12-31 16:11

The top solution above:

let value = UIInterfaceOrientation.LandscapeLeft.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")

didnt'work for me when I called it in viewDidAppear of the presented view controller. However it did work when I called it in preparForSegue in the presenting view controller.

(Sorry, not enough reputation points to comment on that solution, so I had to add it like this)

查看更多
不流泪的眼
6楼-- · 2018-12-31 16:11

My requirements are

  1. lock all views in portrait mode
  2. use AVPlayerViewController to play video

When video is playing, if it's a landscape then allow the screen to rotate landscape right and landscape left. If it's a portrait then lock the view in portrait mode only.

First, define supportedInterfaceOrientationsForWindow in AppDelegate.swift

var portrait = true
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
    if portrait {
        return .Portrait
    } else {
        return .Landscape
    }
}

Second, in your main view controller, define following functions

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    print("\(#function)")
    return .Portrait
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return .Portrait
}

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

Then, you need to subclass AVPlayerViewController

class MyPlayerViewController: AVPlayerViewController {

    var size: CGSize?

    var supportedOrientationMask: UIInterfaceOrientationMask?
    var preferredOrientation: UIInterfaceOrientation?

    override func viewDidLoad() {
        super.viewDidLoad()

        if let size = size {
            if size.width > size.height {
                self.supportedOrientationMask =[.LandscapeLeft,.LandscapeRight]
                self.preferredOrientation =.LandscapeRight
            } else {
                self.supportedOrientationMask =.Portrait
                self.preferredOrientation =.Portrait
            }
        }
    }

Override these three functions in MyPlayerViewController.swift

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
    return self.supportedOrientationMask!
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
    return self.preferredOrientation!
}

Because user might rotate device landscape left or landscape right, we need to set auto rotate to be true

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

Finally, create MyPlayerViewController instance in your view controller and set the property size value.

let playerViewController = MyPlayerViewController()

// Get the thumbnail  
let thumbnail = MyAlbumFileManager.sharedManager().getThumbnailFromMyVideoMedia(......)

let size = thumbnail?.size
playerViewController.size = size

Initiate your player with proper videoUrl, then assign your player to playerViewController. Happy coding!

查看更多
冷夜・残月
7楼-- · 2018-12-31 16:11

There still seems to be some debate about how best to accomplish this task, so I thought I'd share my (working) approach. Add the following code in your UIViewController implementation:

- (void) viewWillAppear:(BOOL)animated
{
    [UIViewController attemptRotationToDeviceOrientation];
}

- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
    return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}

-(BOOL)shouldAutorotate
{
    return NO;
}

- (UIInterfaceOrientationMask)supportedInterfaceOrientations
{
    return UIInterfaceOrientationMaskLandscapeLeft;
}

For this example, you will also need to set your allowed device orientations to 'Landscape Left' in your project settings (or directly in info.plist). Just change the specific orientation you want to force if you want something other than LandscapeLeft.

The key for me was the attemptRotationToDeviceOrientation call in viewWillAppear - without that the view would not properly rotate without physically rotating the device.

查看更多
登录 后发表回答