Only support orientation for modal UIViewController - objective-c

My app has a UINavigationController, it does not support rotation. However I want to show a modal view controller on top of the navigationcontroller that should support rotation. Is this possible? I tried to override shouldAutorotateToInterfaceOrientation on the view controller that is shown modally but that doesn't seem to work.

I think that in addition to shouldAutorotateToInterfaceOrientation of the view controller that is shown modally returning YES, your navigation controller's shouldAutorotateToInterfaceOrientation should be overridden to return YES when that modal view controller is showing, and NO otherwise.

You need to make sure your device generates device orientation notifications (UIDeviceOrientationDidChangeNotification):
make sure:
generatesDeviceOrientationNotifications is set to true
From the Apple docs:
"The window object does much of the work associated with changing the current orientation. However, it works in conjunction with the root view controller to determine whether an orientation change should occur at all and, if so, what additional methods should be called to respond to the change. If this controller is a container, it may rely on a child to decide whether the orientation should occur."
So if you root view controller (UINavigationController?) doesn't support rotations then this might be set to false when your app starts. In which case you will need to turn it back on when appropriate:
UIDevice myDevice = [UIDevice currentDevice];
[myDevice beginGeneratingDeviceOrientationNotifications];

I found the answer in another post: UIViewController inside a root ViewController not rotating
The key is
- (void)addChildViewController:(UIViewController *)childController
and
- (BOOL)automaticallyForwardAppearanceAndRotationMethodsToChildViewControllers

Related

Where to define the -shouldAutorotate method in iOS 6?

I am trying to define the shouldAutorrotate method, but I don't know where to define it as it's never being called. I have a UITabBarController, with three tabs (each one, with a root navigation controller) in my project. My aim is to disable autorotation in some specfic cases.
Thank you!
OK, solved. What I did: In my topmost navigation controller I defined
-(BOOL)shouldAutorotate {return [self.visibleViewController shouldAutorotate];}
and then, in my next viewController
-(BOOL)shouldAutorotate {return NO;}
Thanks guys!!!
you can write that method in viewcontroller.m file, for which you want to disable autorotation
Here is the code for iOS 6:
#ifdef IOS_NEWER_OR_EQUAL_TO_6
-(BOOL)shouldAutorotate
{
return NO;
}
#endif
Read the iOS 6.0 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.
So I would first try to implement the behaviour in the app delegate. If that’s too coarse for your use case, I would try to implement the -shouldAutorotate (note the single r!) method for the individual content view controllers. If that does not work (see the quote above), implement this method in your container controller, ie. the tab bar controller or the navigation controller.
See also the WWDC 2012 Session #236, The Evolution of View Controllers on iOS.

Minimum steps to get autorotate to work

I've trying to implement autorotate but my app is not listening to me!
The app has a tab bar controller which supervises 3 view controllers. The tab bar is created programatically in the app delegate. Each of the view controllers has this standard simple method:
- (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
The app delegate looks like this:
self.tabBarController = [[UITabBarController alloc] init];
self.tabBarController.viewControllers = [NSArray arrayWithObjects:gameVC, settingsVC, helpVC, nil];
self.window.rootViewController = self.tabBarController;
In addition, in the target summary area I have all 4 orientations for both the iPad and iPhone activated.
In the simulator, no rotation occurs with either device. I seem to be missing something. Perhaps one more setting is needed? Something out of order? There is nothing else in the project related to rotating views.
The only thing that you seemed to not have said in your response that I can think of is changing the device orientations under your info.plist. I know from personal experience that if you click on the supported device orientations in the target summary area, it might not actually change it in the Info property list. Check and make sure that all four are selected in the property list by doing the following:
Go to your Info.plist
Look under Supported interface orientations and Supported interface orientations (iPad)
Make sure that it has 4 strings under both: Portrait (bottom home button), Portrait (top home button), Landscape (left home button), Landscape (right home button)
User a ViewController for super purpose, and then inheritance it in the each of view controllers. In the super ViewController add this
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return YES;
}
So, you just need to do once to make them autorotate
from http://developer.apple.com/library/ios/#DOCUMENTATION/WindowsViews/Conceptual/ViewControllerCatalog/Chapters/TabBarControllers.html#//apple_ref/doc/uid/TP40011313-CH3-SW26
Tab bar controllers support a portrait orientation by default and do
not rotate to a landscape orientation unless all of the contained view
controllers support such an orientation. When a device orientation
change occurs, the tab bar controller queries its array of view
controllers. If any one of them does not support the orientation, the
tab bar controller does not change its orientation.
#Zack #AlexanderZats This was subtle. I was reading this SO answer which brought me here This 2nd link is a great discussion of different possible reasons an app may not rotate. The last point caught my attention. Sure enough, I was overriding initWithNibName and not calling super on it. I think this ultimately meant that the the VCs were not in the responder chain. A huge thanks to all who gave me ideas and suggestions!

Removed default scene from storyboard cant rotate!\

So I created a universal app, when I go into my iPad storyboard and remove the only scene that was created then add something like a navigation controller, tableview controller or even a regular view controller, I can no longer rotate my application in simulator. I have made no code changes at this point. I verified that my shouldAutorotateToInterfaceOrientation method hasnt changed. Is there a setting that I am missing that I have to set in the scene to allow it to rotate?
You probably forgot to set the new controller's class to something that has shouldAutorotateToInterfaceOrientation

Multiple Presented View Controllers and Rotation on iPad

I have recently run into an issue when porting some code from an iPhone app over to the iPad. It may be related to the issue described here, though I found the explanations/solutions of that question unsatisfactory. The scenario is this:
View controller 'A' (The Root View Controller) presents a modal view controller (call it 'B') with the "Form Sheet" modal presentation style.
View controller B presents view controller 'C' with the "Full Screen" modal presentation style.
The iPad is rotated while view controller C is the top-most presented view controller.
Upon dismissal of C, B is re-displayed, but has the incorrect orientation.
As far as I can tell, there should not be an issue with chaining of multiple presented view controllers -- in fact, this behavior is explicitly supported in the Presenting View Controllers from Other View Controllers documentation. I have also read the following statement in the iOS 5 Release Notes:
Rotation callbacks in iOS 5 are not applied to view controllers that are presented over a full screen. What this means is that if your code presents a view controller over another view controller, and then the user subsequently rotates the device to a different orientation, upon dismissal, the underlying controller (i.e. presenting controller) will not receive any rotation callbacks. Note however that the presenting controller will receive a viewWillLayoutSubviews call when it is redisplayed, and the interfaceOrientation property can be queried from this method and used to lay out the controller correctly.
As far as I can tell, this does not occur -- View controller B receives a call to -shouldAutoRotateToInterfaceOrientation but the value the interfaceOrientation parameter in this call is the value of view controller B's interface orientation when it presented view controller C, not the value of C's interface orientation upon dismissal. Since we're on an iPad, all these view controllers return YES in -shouldAutoRotateToInterfaceOrientation. Thus the bounds of B's view never change, so -willLayoutSubviews is never called.
I have tried saving the orientation of view controller C in a callback to B before B dismisses it, and then using that information the next time -shouldAutoRotateToInterfaceOrientation is called and returning YES only for the orientation of C when it is dismissed. This fixes, the broken UI that appears without making this check, but view controller B does not update its interface orientation to this value, so subsequent modal presentations will animate in/out from the wrong side of the device.
Has anyone been able to successfully get a view controller configuration like this working? It doesn't seem like that unusual of a scenario, so I am a little surprised that it isn't working as I initially expected it to.
Thanks in advance.
In my opinion multiple chained modal view controllers result in a confusing and annoying user experience if you don't use a navigation controller. I think View controller B should be in a navigation controller (you don't have to show the nab bar if you don't want).
Modal presentation is really supposed to be for single dead-ended entities (a single view controller or a navigation controller containing multiple children view controllers).
Out of interest, are you saying that this works fine on iPhone but not on iPad? Or did you not allow rotation on the iPhone version?
I've also found this thread which says that presenting your modal view controllers from the root view controller may help.
I have worked on multiple modal view controllers being presented on iPhone. There is no problem with layout, unless there is something wrong with my own code for handling multiple orientations. Auto rotation methods actually never get called when view controller is behind another view controller, so I would also adjust layout on viewWillAppear: as well.
On viewWillAppear:, willRotateToInterfaceOrientation:duration:, and didRotateToInterfaceOrientation:, I would adjust the layout to the correct orientation as needed, for example:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self switchToInterfaceOrientation:self.interfaceOrientation];
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[super willRotateToInterfaceOrientation:interfaceOrientation duration:duration];
[self switchToInterfaceOrientation:toInterfaceOrientation];
// be careful, self.view.frame is still according to self.interfaceOrientation
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
// self.view.frame is updated
// update something else here
}
- (void)switchToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// update your layout here
}
I'm not sure about how the above code would behave on view controllers on iPad. Since it supports view controller with different modal presentation styles, there might be some surprises.

UIViewController shouldAutorotateToInterfaceOrientation

I had a question regarding shouldAutorotateToInterfaceOrientation:
I have a grid view controller and I have put a breakpoint in the autorotate method it see if the method was being called. Turns out, even if I rotate the device (testing on simulator), the method is not being called.
Can anyone suggest me how to investigate this problem? Thanks.
Ensure that your view controller is either set as the window's rootViewController, or it is contained within a container view controller that is set as that.
Your issue will be that it is not receiving the window notification which is passed to the root view controller, and then further down to each of its children, and then down to their children etc.
If you have made your own custom container view controllers, ensure they all forward the message on to their children appropriately. Somewhere in that chain is a break.