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
How to Add Followed Text to Uitextfield
Why Swift Use a Struct as Dictionary Key Instead of a String Here
Change Selected Row Label Color in Picker View Swift 1.2
Gmsmapview Animatetocameraposition Zoom in - Zoom Out Animation
How to Convert Bytes to Half-Floats in Swift
Uitextview as Inputaccessoryview Doesn't Render Text Until After Animation
Using Huffman Coding to Compress Images Taken by the iPhone Camera
Circular Button Becomes Rounded Rectangle After Size Increase
I Cannot Access My Array Objects Anywhere, How to Access Them in Swift
Why Do I Get an Mkerrordomain Error When Doing a Local Search
How to Get Only Keys from Firebase
Animated View Floats into Place Weirdly Swiftui
Error in Facebook Login, iOS 9, Swift
Crashing While Collection View Cell Deletion Because of "Uicollectionviewlayoutattributes"
Deinit Method Is Not Called in Xcode 10 Beta 6 Playground
How to Read References Given by Ptr_Refs in iOS
Google Cloud Messaging Bridging Header Import Fails
Swift Save Viewcontroller State Using Coder Beyond App Close