Stop autorotation of UIViewController at creation time - ios

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.

Related

Presenting avplayer horizontally

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.

ViewController and SubView are not rotating correctly

I am working on a landscape view for a currently existing application. I believe I have autoRotate, supported interface, etc set up correctly, I am actually reusing code that works with a much simpler app. However when the simulator is rotated into landscape mode, the correct view loads, but the status bar and view stay with the short edge of the iPad. I've attached a screenshot and code. Is the problem with a view controller higher up in the chain, or the appdelegate? I've traced the called controllers in the debugger and it appears they are dismissed once this page is loaded. I am fairly new-ish to obj-c so it is possible this is something simple I am missing, but I have checked all attributes for the .xib file and everything looks copasetic.
Some of the code:
-(BOOL)shouldAutorotate
{
return NO;
}
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskAll;// | UIInterfaceOrientationMaskPortraitUpsideDown;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Overriden to allow any orientation.
return NO;
}![enter image description here][2]
-(void)orientationChanged{
UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if ((interfaceOrientation==UIInterfaceOrientationPortrait)||(interfaceOrientation ==UIInterfaceOrientationPortraitUpsideDown)){
self.view = self.portraitView;
} else {
self.view = self.landscapeView;
}
}
EDITS - This problem occurs on both iOS 7.1 and 6.1 and this is the first time any screen in the app supports a landscape view. To clarify the views, the portrait and landscape views are separate Views in single .xib file. The file owner class is set to the correct view controller class, and the parent view controller, a sales screen, should not rotate. It does not have a landscape view, but even with its autoRotate methods set to return YES the subview does not orient correctly.
Like 0x7ffffffff already said you need to allow rotation in your shouldAutorotate functions. Also you need to set up the supported rotation directions in your project setup.
First go to your project's settings:
Next you need to select all the orientations you want to support:
Another that is very important: ONLY the root View Controller will receive rotation events. If you nest a View Controller inside a View Controller then that nested Controller will not receive those events unless you wire them up manually from the parent. That's why I usually don't nest ViewControllers but use ad-hoc NSObjects or UIView implementations for nested views.
Last but not least: make sure your device is not rotation-locked: http://www.iphonefaq.org/archives/972915
The problem was occurring because a subclass of the customerView was not receiving the rotation notification. After tracking that class down it was a matter of setting up the NSNotificationCenter for orientation changes and then allowing autoRotation and supprotedInterfaceOrientations.

How to configure SWRevealViewController/PKRevealController/etc to handle orientation and presentation changes

I am using SWRevealViewController (https://github.com/John-Lluch/SWRevealViewController) to handle switching between two controllers, a "front" and a "rear".
The front controller is a UINavigationController and the rear controller is just a plain UIViewController that displays a list of menu items. The front UINavigationController pushes an instance of a view controller named FrontViewController. The rear UIViewController is an instance of RearViewController. The instance of SWRevealViewController is set as the root view controller once it is configured with the front and rear controllers, the delegate property of the reveal controller is set to the app delegate itself.
In both FrontViewController and RearViewController I am overriding shouldAutorotate and returning NO as well as overriding supportedInterfaceOrientations and returning UIInterfaceOrientationMaskPortrait.
However the app auto rotates and goes into Landscape while these two views are displaying when I rotate the device.
supportedInterfaceOrientations seems to only be invoked in FrontViewController but the value is not honored and the device rotates into landscape orientation.
I can't simply set the entire app to Portrait either because I have other detail views that I do want to support Landscape (movie player, etc).
How can I get SWRevealViewController working so I can restrict the app to Portrait in certain child views of the controller?
I have also noticed that the presentation changes are not being honored as well. In RearViewController I am overriding prefersStatusBarHidden to return YES but this method is never invoked. Similarly in FrontViewController I am overriding preferredStatusBarStyle to return UIStatusBarStyleLightContent but this method is never called either.
I have UIViewControllerBasedStatusBarAppearance set to YES in my plist.
UPDATE:
I have tried to use PKRevealController as suggested in the comments but the behavior is exactly the same. Supported orientations and status bar styles are completely ignore. supportedInterfaceOrientations on FrontViewController is the only override invoked but the return value of UIInterfaceOrientationMaskPortrait is not honored.
UPDATE 2:
I'm thinking that this is simply a limitation of these controls and they pretty much expect the support orientations to be the same throughout the application. I did however try MFSideMenu (https://github.com/mikefrederick/MFSideMenu) and it seems to handle supported orientations in different child views exactly as you would expect it to. I still don't have the status bar visibility and styles working, unfortunately.
To achieve this while not avoiding SWRevealViewController: Inside of it there is implemented method supportedInterfaceOrientations, which needs to be edited. I have done this:
- (NSUInteger)supportedInterfaceOrientations
{
UINavigationController* frontNavigationController = (UINavigationController*)self.frontViewController;
if ([frontNavigationController.visibleViewController isKindOfClass:[VCgallery class]]) {
return UIInterfaceOrientationMaskAll;
}
return UIInterfaceOrientationPortrait | UIInterfaceOrientationPortraitUpsideDown;
}
All my viewControllers are portrait only except those that are of class VCgallery, which can be both portrait and lanscape.
The solution ended up being to subclass PKRevealController and override supportedInterfaceOrientations, shouldAutorotate, preferredInterfaceOrientationForPresentation, prefersStatusBarHidden, and preferredStatusBarStyle.
While MFSideMenu did this for orientation it did not support status bar configurations. I also ran into a major bug with MFSideMenu that prevented me from using it in my project.
I made each method I override return a value from the appropriate controller depending on the circumstance. In the case orientation I return the value from self.frontViewController.topViewController (since I am using a UINavigation controller). Status bar style and visibility came from either self.frontViewController or self.leftViewController depending on the current state.
The same solution probably would have worked for SWRevealViewController as well but I preferred the API design of PKRevealController.
I figured subclassing would work from the beginning but I assumed that such a common scenario would be handled in the configuration of these controls.

Understanding iOS6 autorotation in more depth

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

ipad app won't rotate in IOS6

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 {}

Resources