I'm creating an iPhone-only app using the latest version of Swift and Xcode, with 3 tabBar sections in the app, and in only one tab bar I want the user to have its screen fixed to landscape. I've tried almost all possible solutions I found on StackOverflow and some other websites, all not seeming to work(including this one: How to lock orientation of one view controller to portrait mode only in Swift). Anyone know why?
This is the view controller of the view that I want to be fixed in landscape orientation:
import UIKit
#objcMembers class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
themes()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
//Some Code
///////////////////////////////////////
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
override var shouldAutorotate: Bool {
return false
}
///////////////////////////////////////
}
You can force application to rotate orientation using below code,
UIDevice.current.setValue(.landscapeRight, forKey: "orientation")
Please implement below delegate method in AppDelegate and return landscape mask when you want application to rotate to landscape mode,
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask
Related
I'm working on an app that requires locking the orientation in each view controller.
I read this article, but it did not work for me.
In the project settings, I set the device orientation to Portrait, Landscape Left, and Landscape right.
I have two view controllers, VC1 and VC2, and I want to lock the VC1's orientation vertically only. I want to lock the VC2's orientation, on the other hand, horizontally only.
Then I added the following code in VC1.
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait //return the value as per the required orientation
}
override var shouldAutorotate: Bool {
return false
}
But, I can still able to rotate the VC1 horizontally...
Side note: VC1 contains a navigation bar and a tab bar.
If you know how to solve this issue, please let me know...
You can force orientation with few steps:
Firstly, In your AppDelegate define a orientation property and conform supportedInterfaceOrientationsFor
var orientationLock = UIInterfaceOrientationMask.portrait
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return self.orientationLock
}
Then declare utility struct to set orientation using KVO:
struct AppOrientationUtility {
static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation: UIInterfaceOrientation) {
self.lockOrientation(orientation)
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}
How to use:
//For portrait
AppOrientationUtility.lockOrientation(UIInterfaceOrientationMask.portrait, andRotateTo: UIInterfaceOrientation.portrait)
//For landscape
AppOrientationUtility.lockOrientation(UIInterfaceOrientationMask.landscapeRight, andRotateTo: UIInterfaceOrientation.landscapeRight)
I am trying to prevent rotation (lock it to say, portrait) in a specific VC that is
embedded in a navigation controller.
I am currently doing this:
To UINavigationController
extension UINavigationController {
public override func supportedInterfaceOrientations() -> Int {
return visibleViewController.supportedInterfaceOrientations()
}
}
In my VC:
class ViewController: UIViewController {
override func supportedInterfaceOrientations() -> Int {
return Int(UIInterfaceOrientationMask.Landscape.rawValue)
}
}
However, I have issues when we go to another VC (embedded in another nav controller) is presented which supports both landscape and portrait. Suppose, the user rotates in the new screen to landscape. And clicks back to go to original screen. The app is now presented in landscape as opposed to portrait defined in its supportedInterfaceOrientations override. How do I prevent this erroneous behaviour?
I read in iOS 11, we should use viewWillTransition(to:with:) to handle rotation (and locking as well). In UIViewController documentation
“As of iOS 8, all rotation-related methods are deprecated. Instead,
rotations are treated as a change in the size of the view controller’s
view and are therefore reported using the viewWillTransition(to:with:)
method. When the interface orientation changes, UIKit calls this
method on the window’s root view controller. That view controller then
notifies its child view controllers, propagating the message
throughout the view controller hierarchy.”
Can you give directions on how to achieve it?
You can use this cool utility that I've been using.
struct AppUtility {
static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}
/// OPTIONAL Added method to adjust lock and rotate to the desired orientation
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
self.lockOrientation(orientation)
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}
You can also rotate your screen and at the same time, lock it to that orientation. I hope this helps!
You can use supportedInterfaceOrientationsFor
Define this in your AppDelegate
var restrictRotation = Bool()
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if restrictRotation {
return .portrait
}
else {
return .all
}
}
Put below code in your ViewController
func restrictRotation(_ restriction: Bool) {
let appDelegate = UIApplication.shared.delegate as? AppDelegate
appDelegate?.restrictRotation = restriction
}
call above function in your ViewController ViewwillAppear like this.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.restrictRotation(true) // TRUE MEANS ONLY PORTRAIT MODE
//OR
self.restrictRotation(false) // FALSE MEANS ROTATE IN ALL DIRECTIONS
}
I have a simple camera app.
The Camera Preview screen is a viewcontroller (name ViewController) which I locked the orientation via
this code (can't find the StackOverflow link to this code), The viewcontroller is embedded in a navigation controller:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if let navigationController = self.window?.rootViewController as? UINavigationController {
if navigationController.visibleViewController is ViewController {
return UIInterfaceOrientationMask.portrait
} else {
return UIInterfaceOrientationMask.all
}
}
return UIInterfaceOrientationMask.portrait
}
When the camera starts up, the Camera Preview is locked in .portrait orientation, which is what I desire.
However, if I hit the library button, which segues me to another screen,
and change the orientation of the library screen by rotating my physical device, and THEN return back to my Camera Preview screen via the "back" button, the orientation is broken. It's in landscape mode, instead of portrait mode.
How do I get a hard lock?
Just incase, I made a dummy model of my code here (too long to post here), where you guys can test it out yourself
https://github.com/bigmit2011/Testing-Camera
EDIT:
If the app crashes just try running it one more time.
For some reason the first time the app is ran it crashes. Thank you.
EDIT2:
Attempting to set embedded ViewController as a delegate of UINavigationController.
However, I don't quite understand if I'm doing it correctly:
class ViewController: UIViewController, UINavigationControllerDelegate {
var navigation: UINavigationController?
self.navigation.delegate = self // doesn't seem to work
func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
return .portrait
}
Do I need to create a separate swift file for the UiNavigationController?
EDIT3:
Following Matt's answer code is as follows:
class CameraViewController :UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
self.navigationController?.delegate = self
super.viewDidLoad()
func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
return .portrait
}
However, this seems to lock everything in portrait mode not just that single viewController embedded within Navigation Controller.
You are making two mistakes.
First, you are trying to govern the orientation of individual view controllers from the app delegate. That's wrong. The app delegate governs the totality of possible orientations for the whole app. But as for view controllers, each individual view controller governs its own orientation. Just implement supportedInterfaceOrientations in each of your two view controllers (not in the app delegate).
Second, your first view controller is in a navigation interface. Therefore it is not the top-level view controller and cannot govern orientation directly (with supportedInterfaceOrientations). The correct way to govern the orientation of a navigation interface is to set yourself as the navigation controller's delegate and implement navigationControllerSupportedInterfaceOrientations(_:).
I also couldn't run your app. But I tested this in Xcode with a main VC, a 2nd VC and embedded the main VC in a navigation controller. I then followed the advice in this article Selective rotation lock in iOS
You add the following to AppDelegate:
var enableAllOrientation = false
func application(_ application: UIApplication,supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if (enableAllOrientation == true){
return UIInterfaceOrientationMask.allButUpsideDown
}
return UIInterfaceOrientationMask.portrait
}
Then in the VC that allows allButUpsideDown (or change this to what you want), you add the following code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.enableAllOrientation = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.enableAllOrientation = false
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
I put this in the 2nd VC. Ran the app in the simulator, segued to the 2nd VC, changed the orientation to landscape then hit back and the main VC was locked in portrait.
I have created an application for iPhone, using swift, that is composed from many views embedded in a navigation controller. I would like to lock the main view to Portrait orientation and only a subview of a navigation controller locked in Landscape orientation.
Here is an example of what i mean:
UINavigationController
UiViewController1 (Locked in Portrait) Initial view controller with a button placed on the navigation bar that give to the user the possibility to access to a lists where can be selected other views
UIViewController2 (Locked in Landscape)
UiViewController3 (Portrait and Landscape)
UiViewController4 (Portrait and Landscape)
...
...
How Can i do that?
According to the Swift Apple Docs for supportedInterfaceOrientations:
Discussion
When the user changes the device orientation, the system calls this method on the root view controller or the topmost presented view controller that fills the window. If the view controller supports the new orientation, the window and view controller are rotated to the new orientation. This method is only called if the view controller's shouldAutorotate method returns true.
Your navigation controller should override shouldAutorotate and supportedInterfaceOrientations as shown below. I did this in a UINavigationController extension for ease:
extension UINavigationController {
public override func shouldAutorotate() -> Bool {
return true
}
public override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return (visibleViewController?.supportedInterfaceOrientations())!
}
}
And your main viewcontroller (portrait at all times), should have:
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
Then, in your subviewcontrollers that you want to support portrait or landscape:
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.All
}
Edit: Updated for iOS 9 :-)
Also answered [here]
Things can get quite messy when you have a complicated view hierarchy, like having multiple navigation controllers and/or tab view controllers.
This implementation puts it on the individual view controllers to set when they would like to lock orientations, instead of relying on the App Delegate to find them by iterating through subviews or relying on inheritance.
Swift 3
In AppDelegate:
/// set orientations you want to be allowed in this property by default
var orientationLock = UIInterfaceOrientationMask.all
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return self.orientationLock
}
In some other global struct or helper class, here I created AppUtility:
struct AppUtility {
static func lockOrientation(_ orientation: UIInterfaceOrientationMask) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
delegate.orientationLock = orientation
}
}
/// OPTIONAL Added method to adjust lock and rotate to the desired orientation
static func lockOrientation(_ orientation: UIInterfaceOrientationMask, andRotateTo rotateOrientation:UIInterfaceOrientation) {
self.lockOrientation(orientation)
UIDevice.current.setValue(rotateOrientation.rawValue, forKey: "orientation")
}
}
Then in the desired ViewController you want to lock orientations:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
AppUtility.lockOrientation(.portrait)
// Or to rotate and lock
// AppUtility.lockOrientation(.portrait, andRotateTo: .portrait)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Don't forget to reset when view is being removed
AppUtility.lockOrientation(.all)
}
If your view is embedded in navigationcontroller in storyboard set the navigation controller delegate UINavigationControllerDelegate and add the following method
class ViewController: UIViewController, UINavigationControllerDelegate {
func navigationControllerSupportedInterfaceOrientations(navigationController: UINavigationController) -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
}
Update: if you're having trouble to set orientation right after the app launches in iOS 10, try do it in ObjC instead of Swift, and with class MyNavigationController: MyNavigationControllerBase:
#implementation ABNavigationControllerBase
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
#end
Swift 3:
class MyNavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
}
override var shouldAutorotate: Bool {
return false
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
return .portrait
}
}
Same JasonJasonJason answer in Swift 4.2+ (It worked correctly with iOS 11)
1- Override shouldAutorotate and supportedInterfaceOrientations as shown below.
extension UINavigationController {
open override var shouldAutorotate: Bool {
return true
}
open override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return (visibleViewController?.supportedInterfaceOrientations)!
}
}
2- And your main viewcontroller (portrait at all times), should have:
public override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait
}
3- Then, in your subviewcontrollers that you want to support portrait or landscape:
public override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.all
}
The important think is to describe supported interface orientations for whole application in AppDelegate. For example to block all views to portrait just do this:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask{
return UIInterfaceOrientationMask.portrait
}
Lots of people are looking just for this answer by googling threw this question so I hope you can excuse me.
Works in Swift 3.0
In the main controller where you want portrait,
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
self.orientation = UIInterfaceOrientationMaskPortrait;
//Or self.orientation = UIInterfaceOrientationPortraitUpsideDown
}
and in subVC where you want Landscape use
self.orientation = UIInterfaceOrientationLandscapeLeft:
self.orientation = UIInterfaceOrientationLandscapeRight:
or you can override this method
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
This is how i would do it with Obj-c in iOS7, i think this code would work in iOS8 too
Edited for swift2.0 :
override func shouldAutorotate() -> Bool {
return false
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return [.Portrait, .PortraitUpsideDown]
}
Here is the Swift Update :-
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.Portrait
}
This is the syntax for Swift 3 (XCode 8.2 beta), where these methods where converted to properties:
extension UINavigationController {
override open var shouldAutorotate: Bool {
return false
}
}
class ViewController: UIViewController {
/*
...
*/
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait
}
}
This requires two things
Informing the controller of its support for rotation.
Enforcing rotation and then handing over responsibility to a controller that knows its support for rotation.
Declare an extension on view controller that forces orientation to portrait.
extension UIViewController {
func forcePortrait() {
UIView.setAnimationsEnabled(false)
UIDevice.current.setValue(UIInterfaceOrientation.portrait.rawValue, forKey: "orientation")
UIView.setAnimationsEnabled(true)
}
}
Any view controller that is locked to portrait could inherit traits.
class PortraitViewController: UIViewController {
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait }
override open var shouldAutorotate: Bool { return false }
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
forcePortrait()
}
}
Any view controller that is capable of rotating between portrait and landscape can inherit those traits.
class LandscapeViewController: UIViewController {
override open var supportedInterfaceOrientations: UIInterfaceOrientationMask { return [.landscape, .portrait] }
override open var shouldAutorotate: Bool { return true }
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// if leaving for a portrait only screen, force portrait.
// forcePortrait()
}
}
If your landscape view controller is about to segue to a portrait locked screen. Be sure to lock the orientation just before leaving. Then rely on the portrait view controller to enforce its own lack of rotation.
Here is the working code to lock the orientation:
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
More information:
https://www.hackingwithswift.com/example-code/uikit/how-to-lock-a-view-controllers-orientation-using-supportedinterfaceorientations
You do have to apply this to your top view controller. However, you can do it in a clean/easy way by subclassing your top view controller and setting a variable within it that to references every time you make a call to:
shouldAutoRotate()
which is called when the device detects an orientation change and precedes the call to:
supportedInterfaceOrientations()//this is only called if shouldAutoRotate() returns true
For example, say my top view controller is a TabBarController:
class SomeSubclassTabBarViewController: UITabBarController { //This subclass allows us to pass along data to each of the tabBars
var dataArray = StoredValues()//also useful for passing info between tabs
var shouldRotate: Bool = false
override func shouldAutorotate() -> Bool { //allow the subviews accessing the tabBarController to set whether they should rotate or not
return self.shouldRotate
}
}
Then within the view which should have the ability to rotate the screen, set the viewWillAppear() and viewWillDisappear() like so:
override func viewWillAppear(animated: Bool) { //set the rotate capability to true
let sharedTabBarController = self.tabBarController as SomeSubclassTabBarViewController
sharedTabBarController.shouldRotate = true
}
override func viewWillDisappear(animated: Bool) {
let sharedTabBarController = self.tabBarController as SomeSubclassTabBarViewController
sharedTabBarController.shouldRotate = false
}
and just in case your app crashes while on this screen, it's probably a good idea to explicitly set the shouldRotate: Bool value on each of your views within the viewWillLoad() method.
In iOS8, if you want to lock some especific ViewController, create an extension of UINavigationController (in the case you use it):
extension UINavigationController {
public override func shouldAutorotate() -> Bool {
if visibleViewController is YourViewController {
return false
}
return true
}}
If your iOS application is in Landscape mode only and You want to use camera in landscape mode of application then please try for below solution.
Step 1:
In your appdelegate.m class
-(UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone){
NSString *captionVal = [TMUtils getValueInUserDefault:#"CAPTION"];
if ([captionVal isEqualToString:#"Camera"]) {
return UIInterfaceOrientationMaskPortrait;
}else{
return UIInterfaceOrientationMaskLandscapeRight;
}
}else{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
}
Here you can take shared preference value CAPTION as keyValue pair and store the value "Camera".
Step 2:
Now in your viewController.m class in camera button Action set shared preference value and open new ViewController which will be having camera functionality.
[TMUtils setValueInUserDefault:#"CAPTION" value:#"Camera"];
Step 3:
In Camera functionality viewController.m class set storyboard with UIImageView and back button.
Now in ViewDidLoad of camera functionality viewController.m set
- (void)viewDidLoad {
[super viewDidLoad];
if (![UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
NSLog(#"Error");
} else {
UIImagePickerController *pickerController = [[UIImagePickerController alloc] init];
pickerController.modalPresentationStyle = UIModalPresentationCurrentContext; //this will allow the picker to be presented in landscape
pickerController.delegate = self;
pickerController.allowsEditing = YES;
pickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
[self presentViewController:pickerController animated:YES completion:nil];
}
}
Now in UIImagePickerController delegate method set image to UIImageView
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *chosenImage = info[UIImagePickerControllerOriginalImage];
self.cameraImage.image = chosenImage;
[picker dismissViewControllerAnimated:YES completion:NULL];
}
Now on camera back UIButton
- (IBAction)cameraBtnAction:(id)sender {
[TMUtils setValueInUserDefault:#"CAPTION" value:#"NOCamera"];
[self dismissViewControllerAnimated:YES completion:nil];
}
It will always check according to shared preference value in delegate class function for supportedInterfaceOrientationsForWindow in reference to that value it allow camera functionality ViewController to open camera in Portrait mode and rest it will again go back to Landscape mode, which will completely work fine.
When UIKit detects a change in device orientation, it uses the UIApplication object and the root view controller to determine whether the new orientation is allowed. If both objects agree that the new orientation is supported, then auto-rotation occurs. Otherwise, the orientation change is ignored.
By default, the UIApplication object sources its supported interface orientations from the values specified for the UISupportedInterfaceOrientations key in the applications' Information Property List. You can override this behavior by implementing the application:supportedInterfaceOrientationsForWindow: method in your application's delegate. The supported orientation values returned by this method only take effect after the application has finished launching. You can, therefore, use this method to support a different set of orientations after launch.
Allowing your app to rotate into portrait after launch.
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskAllButUpsideDown;
}
source: https://developer.apple.com/
First of all, this isn't a duplicate. I've looked at all of the questions related to this on SO and none of them work for me. Hopefully it's just because I'm new to iOS development but I suspect this isn't possible in my case. I've attached a picture with the view controller circled that I want to disable rotation for.
I've already tried: Subclassing the view controller that I want to disable rotation for and using the shouldAutoRotate method by setting it to NO. Apparently this doesn't work because it's the navigation controller that dictates whether its view controllers can rotate. So, I subclassed UINavigationController and used this class in storyboard instead of the default navigation controller. In this class I've set shouldAutoRotate to NO. It still doesn't work. Don't really know what I'm doing wrong.
When I extend the root view controller with my view controller class with shouldAutoRotate set to NO, it disables rotation...for the whole app. This is not what I want. I only want the rotation to be disabled for the view controller circled in the picture.
Thanks in advance!
Add your AppDelegate.h
#property (nonatomic , assign) bool blockRotation;
AppDelegate.m
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
if (self.blockRotation) {
return UIInterfaceOrientationMaskPortrait;
}
return UIInterfaceOrientationMaskAll;
}
In view Controller that you want to disable rotation
- (void)viewDidLoad
{
[super viewDidLoad];
AppDelegate* shared=[UIApplication sharedApplication].delegate;
shared.blockRotation=YES;
}
-(void)viewWillDisappear:(BOOL)animated{
AppDelegate* shared=[UIApplication sharedApplication].delegate;
shared.blockRotation=NO;
}
Swift 4.2 and later:
In your AppDelegate.swift:
var blockRotation = false
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
if blockRotation {
return .portrait
}
return .all
}
In your viewController:
override func viewDidLoad() {
super.viewDidLoad()
AppDelegate.shared.blockRotation = true
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
AppDelegate.shared.blockRotation = false
}
Thanks to #ozgur for the fix which worked for me. I would 'vote up', but apparently I'm not good enough (whatever!) to vote whether a fix works or not. Anyway, here it is in Swift:
In AppDelegate.swift:
class AppDelegate: UIResponder, UIApplicationDelegate {
var blockRotation: Bool = false
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> Int {
if (self.blockRotation) {
println("supportedInterfaceOrientations - PORTRAIT")
return Int(UIInterfaceOrientationMask.Portrait.rawValue)
} else {
println("supportedInterfaceOrientations - ALL")
return Int(UIInterfaceOrientationMask.All.rawValue)
}
}
In the ViewController that you want to block rotation, add UIApplicationDelegate to your class...
class LoginViewController: UIViewController, UITextFieldDelegate, UIApplicationDelegate {
and then create a reference to the AppDelegate...
var appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
In viewDidLoad, set appDelegate.blockRotation = true:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
appDelegate.blockRotation = true
}
In viewWillAppear, set the orientation to force the device to the chosen orientation (Portrait in this example):
override func viewWillAppear(animated: Bool) {
let value = UIInterfaceOrientation.Portrait.rawValue
UIDevice.currentDevice().setValue(value, forKey: "orientation")
}
Then in viewWillDisappear, or in prepareForSegue, set appDelegate.blockRotation = false:
override func viewWillDisappear(animated: Bool) {
appDelegate.blockRotation = false
}
This worked for me in this exact (multiple view controllers in a Navigation Controller) scenario, after many hours of reading other solutions on this site. Thanks again to #ozgur - I'd up-vote your answer if I could!
Happy trails!
You have to check in your root view controller is rotation allowed in current top controller and return YES or NO in supportedInterfaceOrientations method. So it should be like the following code in your root controller (adapt the code to your case):
- (NSUInteger)supportedInterfaceOrientations
{
return [self.navigationController.topViewController supportedInterfaceOrientations];
}
Then in each view controller add supported interface orientations, for example:
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait;
}
For those of you using Swift 2, you can follow Andre's answer, but in step one, use the code below in your AppDelegate.swift:
class AppDelegate: UIResponder, UIApplicationDelegate {
var blockRotation: Bool = false
func application(application: UIApplication, supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask {
if (self.blockRotation) {
print("supportedInterfaceOrientations - PORTRAIT")
return UIInterfaceOrientationMask.Portrait
} else {
print("supportedInterfaceOrientations - ALL")
return UIInterfaceOrientationMask.All
}
}
supportedInterfaceOrientationsForWindow: now returns a UIInterfaceOrientationMask instead of an Int.
My case has 3 view controller:
- first view controller: portrait
- second view controller: landscape right (has navigation controller and was presented by first view controller)
- third view controller: portrait (has navigation controller and was pushed by second view controller )
And this is my solution in swift 3:
------------------------------------
At AppDelegate:
- Add this property to save your setting
private var orientation: UIInterfaceOrientationMask = .portrait
-Then create a function to set rotate for your device:
func rotateScreen(orientation: UIInterfaceOrientationMask) {
self.orientation = orientation
var value = 0;
if orientation == .landscapeRight {
value = UIInterfaceOrientation.landscapeRight.rawValue
}else {
value = UIInterfaceOrientation.portrait.rawValue
}
UIDevice.current.setValue(value, forKey: "orientation")
}
- Finally, implement support orientation method:
func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
return self.orientation
}
--------------------------------
Then you can call like this before display destination view controller
(UIApplication.shared.delegate as! AppDelegate).rotateScreen(orientation: .landscapeRight)
For example, in my case: When user tap on button at the First to present the Second, I'll do like this
(UIApplication.shared.delegate as! AppDelegate).rotateScreen(orientation: .landscapeRight)
self.present(navigation, animated: true, completion: nil)
And the close button at the Second I'll do like this
(UIApplication.shared.delegate as! AppDelegate).rotateScreen(orientation: .portrait)
self.dismiss(animated: true, completion: nil)
That's all! You could never mind using navigation controller or not.
Hope this helps you guys ^__^!
UINavigationController+Rotation Category
When u usign navigation controller SupportedInterfaceOrientaions of ViewController will not work adding this category to ur Project will get the response from your viewController also instead of NavigationController alone.
In my case I have the view controller embedded in a navigation controller, and most of the solutions don't work because the viewcontroller depends on the navigation controller orientation. For this when you create the instance of the view controller you have to cast it to UINavigationController :
let theViewController = UIViewController()
if let navController = theViewController as? UINavigationController {
navController.delegate = self
}
And then add this extension:
extension PlaySplashViewController: UINavigationControllerDelegate {
func navigationControllerSupportedInterfaceOrientations(_ navigationController: UINavigationController) -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait
}
}
In this case this going to set portrait orientation for the navigation controller so the view controller going to use this orientation too.