Force Rotate Uiviewcontroller

Force rotate UIViewController in Portrait status

The short answer is that you cannot because according to the Apple docs here

The system intersects the view controller's supported orientations
with the app's supported orientations (as determined by the Info.plist
file or the app delegate's
application(_:supportedInterfaceOrientationsFor:) method) and the
device's supported orientations to determine whether to rotate.

To implement what you need you should set the allowed orientations to landscape too, then implement the following in your view controllers to allow either the portrait or landscape orientations (or both):

override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait//or UIInterfaceOrientationMask.landscape
}

Then to force a specific orientation you can set the orientation to the UIDevice:

UIDevice.current.setValue(UIInterfaceOrientation.landscapeLeft.rawValue, forKey: "orientation")

How to force view controller orientation in iOS 8?

For iOS 7 - 10:

Objective-C:

[[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"];
[UINavigationController attemptRotationToDeviceOrientation];

Swift 3:

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
UINavigationController.attemptRotationToDeviceOrientation()

Just call it in - viewDidAppear: of the presented view controller.

Force landscape mode in one ViewController using Swift

It may be useful for others, I found a way to force the view to launch in landscape mode:

Put this in the viewDidLoad():

let value = UIInterfaceOrientation.landscapeLeft.rawValue
UIDevice.current.setValue(value, forKey: "orientation")

and,

override var shouldAutorotate: Bool {
return true
}

How to force a UIViewController to Portrait orientation in iOS 6

If you want all of our navigation controllers to respect the top view controller you can use a category so you don't have to go through and change a bunch of class names.

@implementation UINavigationController (Rotation_IOS6)

-(BOOL)shouldAutorotate
{
return [[self.viewControllers lastObject] shouldAutorotate];
}

-(NSUInteger)supportedInterfaceOrientations
{
return [[self.viewControllers lastObject] supportedInterfaceOrientations];
}

- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [[self.viewControllers lastObject] preferredInterfaceOrientationForPresentation];
}

@end

As a few of the comments point to, this is a quick fix to the problem. A better solution is subclass UINavigationController and put these methods there. A subclass also helps for supporting 6 and 7.

Force uiviewcontroller to rotate

To check the orientation use UIDevice Orientation

[[UIDevice currentDevice] orientation]

To change the orientation->

Objective-c:

NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:value forKey:@"orientation"];

Swift:

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

Force View Controller Orientation in iOS 9

I was able to find a solution with the assistance of this answer: Programmatic interface orientation change not working for iOS

My base orientation logic is as follows:

// Local variable to tracking allowed orientation. I have specific landscape and
// portrait targets and did not want to remember which I was supporting
enum MyOrientations {
case Landscape
case Portrait
}
var orientation: MyOrientations = .Landscape

// MARK: - Orientation Methods

override func shouldAutorotate() -> Bool {
return true

}

override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
if self.orientation == .Landscape {
return UIInterfaceOrientationMask.LandscapeRight
} else {
return UIInterfaceOrientationMask.Portrait
}
}

override func preferredInterfaceOrientationForPresentation() -> UIInterfaceOrientation {
if self.orientation == .Landscape {
return UIInterfaceOrientation.LandscapeRight
} else {
return UIInterfaceOrientation.Portrait
}
}

// Called on region and delegate setters
func refreshOrientation() {
if let newOrientation = self.delegate?.getOrientation() {
self.orientation = newOrientation
}
}

Then when I want to refresh the orientation, I do the following:

// Correct Orientation
let oldOrientation = self.orientation
self.refreshOrientation()
if self.orientation != oldOrientation {
dispatch_async(dispatch_get_main_queue(), {
self.orientationRefreshing = true

let vc = UIViewController()

UIViewController.attemptRotationToDeviceOrientation()

self.presentViewController(vc, animated: false, completion: nil)

UIView.animateWithDuration(0.3, animations: {
vc.dismissViewControllerAnimated(false, completion: nil)
})
})
}

This solution has the side effect of causing view[Will/Did]Appear and view[Will/Did]Disappear to fire all at once. I'm using the local orientationRefreshing variable to manage what aspects of those methods are called again.

Force iOS view to not rotate, while still allowing child to rotate

I had this exact problem, and found out quickly there's a lot of bad advice floating around about autorotation, especially because iOS 8 handles it differently than previous versions.

First of all, you don't want to apply a counterrotation manually or subscribe to UIDevice orientation changes. Doing a counterrotation will still result in an unsightly animation, and device orientation isn't always the same as interface orientation. Ideally you want the camera preview to stay truly frozen, and your app UI to match the status bar orientation and size as they change, exactly like the native Camera app.

During an orientation change in iOS 8, the window itself rotates rather than the view(s) it contains. You can add the views of multiple view controllers to a single UIWindow, but only the rootViewController will get an opportunity to respond via shouldAutorotate(). Even though you make the rotation decision at the view controller level, it's the parent window that actually rotates, thus rotating all of its subviews (including ones from other view controllers).

The solution is two UIWindow stacked on top of each other, each rotating (or not) with its own root view controller. Most apps only have one, but there's no reason you can't have two and overlay them just like any other UIView subclass.

Here's a working proof-of-concept, which I've also put on GitHub here. Your particular case is a little more complicated because you have a stack of containing view controllers, but the basic idea is the same. I'll touch on some specific points below.

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var cameraWindow: UIWindow!
var interfaceWindow: UIWindow!

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let screenBounds = UIScreen.mainScreen().bounds
let inset: CGFloat = fabs(screenBounds.width - screenBounds.height)

cameraWindow = UIWindow(frame: screenBounds)
cameraWindow.rootViewController = CameraViewController()
cameraWindow.backgroundColor = UIColor.blackColor()
cameraWindow.hidden = false

interfaceWindow = UIWindow(frame: CGRectInset(screenBounds, -inset, -inset))
interfaceWindow.rootViewController = InterfaceViewController()
interfaceWindow.backgroundColor = UIColor.clearColor()
interfaceWindow.opaque = false
interfaceWindow.makeKeyAndVisible()

return true
}
}

Setting a negative inset on interfaceWindow makes it slightly larger than the screen bounds, effectively hiding the black rectangular mask you'd see otherwise. Normally you wouldn't notice because the mask rotates with the window, but since the camera window is fixed the mask becomes visible in the corners during rotation.

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

Exactly what you'd expect here, just add your own setup for AVCapturePreviewLayer.

class InterfaceViewController: UIViewController {
var contentView: UIView!

override func viewDidLoad() {
super.viewDidLoad()

contentView = UIView(frame: CGRectZero)
contentView.backgroundColor = UIColor.clearColor()
contentView.opaque = false

view.backgroundColor = UIColor.clearColor()
view.opaque = false
view.addSubview(contentView)
}

override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()

let screenBounds = UIScreen.mainScreen().bounds
let offset: CGFloat = fabs(screenBounds.width - screenBounds.height)

view.frame = CGRectOffset(view.bounds, offset, offset)
contentView.frame = view.bounds
}

override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.All.rawValue)
}

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

The last trick is undoing the negative inset we applied to the window, which we achieve by offsetting view the same amount and treating contentView as the main view.

For your app, interfaceWindow.rootViewController would be your tab bar controller, which in turn contains a navigation controller, etc. All of these views need to be transparent when your camera controller appears so the camera window can show through beneath it. For performance reasons you might consider leaving them opaque and only setting everything to transparent when the camera is actually in use, and set the camera window to hidden when it's not (while also shutting down the capture session).

Sorry to post a novel; I haven't seen this addressed anywhere else and it took me a while to figure out, hopefully it helps you and anyone else who's trying to get the same behavior. Even Apple's AVCam sample app doesn't handle it quite right.

The example repo I posted also includes a version with the camera already set up. Good luck!



Related Topics



Leave a reply



Submit