Is there any way to allow landscape mode in only one view controller in an app? I'm presenting it modally like so:
let recViewController = AVPlayerViewController()
recViewController.modalTransitionStyle = .CoverVertical
recViewController.player = AVPlayer(URL: NSURL(string: currentScores[selectedButtonIndexPath.row].recapAvailable))
recViewController.player.play()
self.view.window?.rootViewController?.presentViewController(recViewController, animated: true, completion: nil)
I know I can manually override each view controller to only allow for vertical orientation (with the exception of the above one) but that seems rather tedious.
Is there any way to allow landscape mode in only one view controller in an app?
You have answered the question yourself, see the 'Configuring the View Rotation Settings' in UIViewController class reference. You need to configure your app to support all of the rotations you want, then override each view controller, and yes it is tedious, but it works :)
Handling View Rotations
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
viewWillTransitionToSize:withTransitionCoordinator: 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.
In iOS 6 and iOS 7, your app supports the interface orientations
defined in your app’s Info.plist file. A view controller can override
the supportedInterfaceOrientations method to limit the list of
supported orientations. Typically, the system calls this method only
on the root view controller of the window or a view controller
presented to fill the entire screen; child view controllers use the
portion of the window provided for them by their parent view
controller and no longer participate directly in decisions about what
rotations are supported. The intersection of the app's orientation
mask and the view controller's orientation mask is used to determine
which orientations a view controller can be rotated into.
You can override the preferredInterfaceOrientationForPresentation for
a view controller that is intended to be presented full screen in a
specific orientation.
When a rotation occurs for a visible view controller, the
willRotateToInterfaceOrientation:duration:,
willAnimateRotationToInterfaceOrientation:duration:, and
didRotateFromInterfaceOrientation: methods are called during the
rotation. The viewWillLayoutSubviews method is also called after the
view is resized and positioned by its parent. If a view controller is
not visible when an orientation change occurs, then the rotation
methods are never called. However, the viewWillLayoutSubviews method
is called when the view becomes visible. Your implementation of this
method can call the statusBarOrientation method to determine the
device orientation.
EDIT
You can also look at the UIApplicationDelegate Protocol, it has the method
func application(application: UIApplication,
supportedInterfaceOrientationsForWindow window: UIWindow?) -> UIInterfaceOrientationMask
From the documentation:
Discussion
This method returns the total set of interface orientations
supported by the app. When determining whether to rotate a particular
view controller, the orientations returned by this method are
intersected with the orientations supported by the root view
controller or topmost presented view controller. The app and view
controller must agree before the rotation is allowed.
If you do not implement this method, the app uses the values in the
UIInterfaceOrientation key of the app’s Info.plist as the default
interface orientations.
Related
I want to say in each view controller which orientation he supports and restrict it to them. This is what I tried:
Use a custom navigation controller like in one of my old posts, but that doesn't seem to work anymore. The solution is similar to iOS 6 - (BOOL)shouldAutorotate not getting called for navigation controllers pushed viewControllers
I also tried Setting Orientation in Xamarin iOS, but the app crashes on the RootViewController. I get a NullReferenceException here.
Furthermore I tried IOS 8 : Restricting orientation on a View Controller from the deleted blog from Shri Chakraborty (shrixamarin). Here the app also crashes if I use application:supportedInterfaceOrientationsForWindow:, because the RootViewController is null. The solution seems to be similar to How do I restrict orientation per view controller in iOS7 in a navigation controller hierarchy
None of the solutions seems to work. Or do I miss something important? How can I restrict the orientation that view controller A is landscape only, view controller B is portrait only and view controller C can be shown in all available orientations?
Overriding GetSupportedInterfaceOrientations() in your view controller should solve the problem:
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations () {
return UIInterfaceOrientationMask.LandscapeRight;
}
The orientations you return here will be intersected with the app's allowed orientations (project properties) or with what you allow in your app delegate. So be sure to specify all orientations there or the maximum set of orientations you want to support.
See here for documentation.
Is there any way to set autorotate behavior of an UIViewController object when it's initiated?
I wanted it to never rotate, so I tried this:
UIViewController *myViewController = [[UIViewController alloc]init];
myViewController.shouldAutorotate = NO;
Seemed logical, but it doesn't work. Get the error message:
No setter method 'setShouldAutorotate:' for assignment to property
Or is it possible only through subclassing?
You should this way:
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
Return Value
A bit mask specifying which orientations are supported.
See UIInterfaceOrientationMask for valid bit-mask values. The value
returned by this method must not be 0.
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 YES.
Override this method to report all of the orientations that the view
controller supports. The default values for a view controller's
supported interface orientations is set to
UIInterfaceOrientationMaskAll for the iPad idiom and
UIInterfaceOrientationMaskAllButUpsideDown for the iPhone idiom.
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:supportedInterfaceOrientationsForWindow: method) to
determine whether to rotate.
Source: UIViewController on iOS Developer Library.
Ok, this is a weird one.
In iOS 8, if a popover is presented from the master panel in a UISplitViewController while in portrait, rotation is disabled. I've run through a bunch of tests and confirmed this is the case.
There's a private method on UISplitViewController, _shouldPreventAutorotation, that gets called when rotating and inspects the presentationController property on the popover's content controller. If this returns a non-nil value, rotation is disabled. If you override the property and return nil, rotation is once again enabled.
Does anyone have any idea why this behaviour was added in iOS 8?
I uploaded test project that demonstrates this behaviour here.
So, after some more investigation and decompiling in Hopper, there's a private method on UISplitViewController that gets called when the device is rotated that determines whether or not rotation should be disabled.
If the master panel is visible, the master panel has a child modal view controller (in this case a popover), and the presented popover's presentationController property returns a non-nil value, then rotation is disabled.
I can override this behaviour by overriding -presentationController on the popover's controller, and return nil. Not sure about any side effects yet but it works.
- (UIPresentationController *)presentationController {
return nil;
}
I have read multiple QAs on this topic and read the documentation, but I would like some confirmation on the answers.
- (BOOL)shouldAutorotate
This method determines if the VC is allowed to autorotate?
-(NSUInteger)supportedInterfaceOrientations
This method determines what orientations the specific View Controller accepts, is able to rotate too...
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
This method determines what the preferred orientation is for. However, it is not called on the VC but on a navigation controller. So if you wish to use you should subclass uinavigationcontroller.
Is this all correct?
What I am trying to do is have one specific controller auto rotate to landscape if an image is passed to it in that orientation. I can get the image orientation without an issue. I have set the shouldAutorotate to YES on that VC, and the supportedInterfaceOrientations is set to all. However it does not rotate.
This is from a UINavigationController that modally presents another UINavigationController on top of it and is the specific controller is about 6th in the stack.
How do I control the preferred orientation for a VC within a UINavigationController which is determined by a value from that View Controller? As the orientation would not always be the same.
Be careful you don't have orientation lock on the device whilst testing!!! Opps
I have a UISplitViewController ipad app. It uses a mainwindow.xib and my subclassed RootView and DetailView controllers. It autorotates properly in all ioses up to 5.1.
I know the interface changed for ios6. When I add the new rotation methods to my detail and rootview classes it still does not rotate.
My supportedInterfaceOrientations method is called, but shouldAutorotate is never called.
Does anyone know how to fix this?
(Always learning that no one can mess you around as badly as another programmer. Thanks Apple.)
Gerry
From the iOS 6 release notes:
Autorotation is changing in iOS 6. In iOS 6, the shouldAutorotateToInterfaceOrientation: method of UIViewController is deprecated. In its place, you should use the supportedInterfaceOrientationsForWindow: and shouldAutorotate methods.
More responsibility is moving to the app and the app delegate. Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate. By default, an app and a view controller’s supported interface orientations are set to UIInterfaceOrientationMaskAll for the iPad idiom and UIInterfaceOrientationMaskAllButUpsideDown for the iPhone idiom.
A view controller’s supported interface orientations can change over time—even an app’s supported interface orientations can change over time. The system asks the top-most full-screen view controller (typically the root view controller) for its supported interface orientations whenever the device rotates or whenever a view controller is presented with the full-screen modal presentation style. Moreover, the supported orientations are retrieved only if this view controller returns YES from its shouldAutorotate method. 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:supportedInterfaceOrientationsForWindow: method) to determine whether to rotate.
The system determines whether an orientation is supported by intersecting the value returned by the app’s supportedInterfaceOrientationsForWindow: method with the value returned by the supportedInterfaceOrientations method of the top-most full-screen controller.
The setStatusBarOrientation:animated: method is not deprecated outright. It now works only if the supportedInterfaceOrientations method of the top-most full-screen view controller returns 0. This makes the caller responsible for ensuring that the status bar orientation is consistent.
I know that's a mouthful but you might also want to check the supported interface orientations sheet in your project's settings:
Try to set up a notification when orientation changed.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
Then implement the rotation inside orientationChanged function.
- (void)orientationChanged:(NSNotification *)notification {}