Auto-rotation support for view added via presentModalViewController? - iphone

It seems that no matter what orientation I am supporting on my views in my app, when I display a view with presentModalViewController, the app snaps into portrait view. How can I support different orientations with a modal view controller?

The controller that you present using presentModalViewController:animated: should support any orientation, typically using this method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
With this method, when you present your controller in landscape orientation, it will correctly appear in landscape orientation, too.

I had same problem. I set TRUE AutorotateInterfaceOrientation but at the begin my modalview was always presented Portrait even if it was Landscape. Only if i changed device orientation (in emulator) it's behavior became correct. But at start no!
I don't know why, but all depends from where you present view.
If you present, for example, from viewDidLoad method, then problem occurs. For solving problem, i call presentModalView from a method that's called in viewDidLoad (apparently, it seems same thing). So, i did something like:
- (void) viewDidLoad
{
[super viewDidLoad];
// add this line
[self performSelector:#selector(presentModal) withObject:nil afterDelay:0];
}
-(void) presentModal{
// here present your view
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES;
}
This works for me

I just solved this problem for myself by adding code in viewDidAppear. I think the reason it didn't show up in the proper orientation for me is because I had the show modal code in viewDidLoad and at that point, I think the view had just loaded but the orientation was not yet set. So, adding it in viewDidAppear worked for me. You will probably also have luck in viewWillAppear (because by that time, it has to know what the orientation, bounds, sizes, etc are).

Related

Understanding iOS 6 Interface orientation change

ADDED:
I see that my question is viewed often without upvotes so I decided that you guys do not get what you search. Redirecting you to question that has really nice answer about
How to handle orientation changes in iOS6
Specific demands to orientation changes:
Restricted rotation
Upvotes are welcome :)
I've created a new project from Master Detail template and trying to start it with landscape orientation.
As you know the
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
method is deprecated and we must use
- (NSUInteger)supportedInterfaceOrientations
and/or
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
Here's my code:
- (NSUInteger)supportedInterfaceOrientations {
NSLog(#"supported called");
return UIInterfaceOrientationMaskAll;//Which is actually a default value
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
NSLog(#" preferred called");//This method is never called. WHY?
return UIInterfaceOrientationLandscapeRight;
}
As you can see I'm trying to return landscape orientation in preferred method but it is never called.
p.s. documentation states:
Discussion The system calls this method when presenting the view
controller full screen. You implement this method when your view
controller supports two or more orientations but the content appears
best in one of those orientations.
If your view controller implements this method, then when presented,
its view is shown in the preferred orientation (although it can later
be rotated to another supported rotation). If you do not implement
this method, the system presents the view controller using the current
orientation of the status bar.
So, the question is: Why the prefferredOrientation method is never get called? And how should we handle different orientations in different controllers?. Thanks!
P.S don't mark the question as duplicate. I've investigated all similar questions and they do not have answer for mine.
About preferredInterfaceOrientationForPresentation
preferredInterfaceOrientationForPresentation is never called because this is not a "presented" view controller. There is no "presentation" involved here.
"Presented" and "presentation" are not some vague terms meaning "appears". These are precise, technical terms meaning that this view controller is brought into play with the call presentViewController:animated:completion:. In other words, this event arrives only if this is what we used to call a "modal" view controller.
Well, your view controller is not a modal view controller; it is not brought into play with presentViewController:animated:completion:. So there is no "presentation" involved, and therefore preferredInterfaceOrientationForPresentation is irrelevant here.
I'm being very explicit about this because I'm thinking that many folks will be confused or misled in the same way you were. So perhaps this note will help them.
Launch into Landscape
In iOS 6, the "Supported Interface Orientations" key in your Info.plist is taken much more seriously than previously. The solution to your overall problem of launching into a desired orientation is:
Make sure that "Supported Interface Orientations" in your Info.plist lists all orientations your app will ever be allowed to assume.
Make sure that the desired launch orientation is first within the "Supported Interface Orientations".
That's all there is to it. You should not in fact have to put any code into the root view controller to manage the initial orientation.
If you would like launch modal view in Landscape Mode, just put this code in presented view controller
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
UIInterfaceOrientation orient = [[UIApplication sharedApplication] statusBarOrientation];
if (UIInterfaceOrientationIsLandscape(orient)) {
return orient;
}
return UIInterfaceOrientationLandscapeLeft;
}
Then, present this controller as usual
UIViewController *vc = [[UIViewController alloc] initWithNibName:nil bundle:nil];
[vc setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
[vc setModalPresentationStyle:UIModalPresentationFullScreen];
[self.navigationController presentViewController:vc animated:YES completion:^{}];
There is a very simple answer: You can only change or fix the interface orientation of a modal presented view controller. If you do so i.e. with a Present Modally segue in Interface builder (or the navigation controller method) you can define the allowed orientations with
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait; // for example else an addition of all allowed
}
This event doesn't fire up when you only push a view controller on the navigation Controller ... so : You don't have a BACK button and need a
[self dismissViewControllerAnimated:YES completion: ^(void) {
}];
to close it.

iphone screen orientation

Hi I am trying to provide screen orientation in my app and I have read many docs online but when I try that It just is a total disaster (I tried emulator and iphone). On one partwhen the screen is rotated to landscape half of the views are not visible any more. Their position is on the left side and right side is empty, plus if I open a next view controller and then rotate again I can see through the second view parts of the first view it just is terrible I don't know how to fix it (Excluding blocking the possibility to lock orientation in portrait).
I know in android you can provide different xml layouts for portrait and landscape. Is there a way to fo that in iphone? If so how?
each of my views are composed of a controller and .xib
There is a "main" controller and the other controllers are called like that:
ayuda=[[AyudaView alloc]
initWithNibName:#"AyudaView"
bundle:nil];
[self.view addSubview:ayuda.view];
also I have already added these metods to the maincontroller:
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
if(UIDeviceOrientationIsValidInterfaceOrientation(orientation)) {
[self handleInterfaceRotationForOrientation:orientation];
}
}
#pragma mark -
#pragma mark Screen Orientation Handling
- (void)handleInterfaceRotationForOrientation:(UIInterfaceOrientation)interfaceOrientation {
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
[self handleInterfaceRotationForOrientation:interfaceOrientation];
return YES;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self handleInterfaceRotationForOrientation:toInterfaceOrientation];
}
as indicated here
and have tried to play with the autosizing param but it only gets worse
Thank you very much
EDIT: ok I have been reading some more:
http://www.geckogeek.fr/iphone-forcer-le-mode-landscape-ou-portrait-en-cours-dexecution.html
http://www.cocoaosx.com/2011/11/10/rotatingviewcontroller-display-a-different-uiviewcontroller-depending-on-the-rotation-of-the-device/
and basically what I have found is that you have to create 2 controllers but this solution is problematic for me because of the nib files. I mean I would also have to create 2 nibs so double the amount of code (or use the library above). What if I have values in my textfields , do I just have to send them around?
So, apart from the comments suggesting you don't have any idea about what's going on in your code (which may or may not be true -- who am I to judge?), I see a few problems.
The first issue is that I don't think you understand MVC and how iOS handles the view hierarchy. You're correct in having each of your views controlled by a controller and designed by a .xib (there are cases where a view doesn't need its own controller, but for simplicity, we'll assume yours do). However, you are incorrect in the way you are presenting those views on screen. A view's controller is responsible for presenting or removing a view from the screen. The view only knows what it displays, such as labels or buttons. The controller is responsible for telling the view what to display on those labels and buttons. Instead of adding the new VC's view as a subview of your main view, push it onto a navigation controller's stack or present the view modally. If you're in your main VC, you can say
ayuda = [[AyudaView alloc] initWithNibName:nil bundle:nil];
[self presentModalViewController:ayuda animated:YES];
I'm thinking that getting the view hierarchy correct might just be enough to solve your landscape/portrait swapping chaos. But while we're on that topic...why are you creating this handleInterfaceRotation method? View Controllers already have a method for this called
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration;
Use this method to handle rotation.

UIViewController orientations

I have a UIViewController instance(viewController1) that I want to lock in landscape mode which I am able to do. On tapping on a button I push another UIViewController(viewController2) which supports both orientations. Now, if user changes the orientation of viewController2 to portrait and goes back to viewController1, viewController1 also changes it's orientation to portrait. How can I avoid that?
Thanks in advance.
Add these methods to the view controllers
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
Thats for the first
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return YES);
}
Thats the second
-(BOOL)shouldAutorotateToInterfaceOrientation:UIInterfaceOrientation)toInterfaceOrientation{
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
return YES;
}
return NO;
}
If those 2 controllers are both implementation of UIViewController both differente classes each other! you can just implement the method shouldAutorotateToInterfaceOrientation, in the first! this should work even when u go back!
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
return YES;
}
return NO;}
You say push so I assume both ViewControllers are in a NavigationController. If so I'll have to disappoint you, what you want isn't possible. Your rotation callbacks are working correctly, they respond to a rotation, you can't force it. What's happening is the correct behavior.
Best solution is to prevent the user from going back when you're in the orientation the previous ViewController doesn't support, hide the back button for example.
A while back I've made my own NavigationController (doesn't inherit from the UIViewController but it can do exactly the same) and I've tried to implement what you're trying to do. Before pushing or popping, if the view of the ViewController that was about to be shown didn't support the current orientation, I transformed the view of that ViewController by 90° and force the orientation of the status bar to the new ViewController's supported orientation.
As soon as the push or pop was complete I'd do a small trick to force the rotation of the device. If you remove the view of the rootViewController from the window and re-add it, the responder chain will be forced to go through all rotation callbacks. When that happened I checked if a ViewController's view was transformed and reset that transformation.
It did work, mostly. But it was messy code and it goes against Apple's current policy that the rootViewController should be responsible of handling the orientation. Also in iOS6 forcing the status bar orientation is guaranteed to work. So I'd really advise against doing this, I've removed this from my own NavigationController too.

When changing UIWindow subviews, orientation is not set

If a view is added to the window, orientation is set as portrait even if the device is in landscape.
If the view is added in the app delegate, application:didFinishLaunchingWithOptions: method, then it works correctly. But if the view is added later it does not.
As an example, I have a routine to switch views. Simplest form is:
- (void)switchToNewViewController:(UIViewController *)newViewController {
if ([[window subviews]count]!=0) {
[[[window subviews]objectAtIndex:0] removeFromSuperview];
}
[window addSubview:newViewController.view];
}
IF this is called from within didFinishLaunching, orientation is correct. If it is not, orientation is portrait.
Simplest case is within didFinishLaunching I have the following two lines
// The following line works
[self switchToNewViewController:fullScreenViewController];
// The following line which delays the method call until later results
// in incorrect orientation
[self performSelector:#selector(switchToNewViewController:) withObject:fullScreenViewController afterDelay:0.1];
Is there a way to make the view have the proper orientation?
Make sure your shouldAutorotateToInterfaceOrientation in the view controllers has the right logic
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return YES; //this supports all orientations
}
If you are linking stuff in InterfaceBuilder also make sure both the view and the viewcontroller are configured to the initial orientation you like. (There is a little arrow in the top right corner to rotate views and view controllers)
If you still have problems, are you using a UINavigationController or similar? UINavigationController needs to be subclassed and shouldAutorotateToInterfaceOrientation implemented if you want to support something other than portait.

willAnimateRotationToInterfaceOrientation not called on popViewControllerAnimated

In the MainViewController I have to adjust some images on orientation change. Easy - just add the code to the willAnimateRotationToInterfaceOrientation: callback
- (void) willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)orientation duration:(NSTimeInterval)duration {
NSLog(#"orientation: %#", name(orientation));
..
Works as expected. While in Portrait I push another orientation aware UIViewController onto the UINavigationController. I rotate to Landscape and all is good.
Then I go back to the MainViewController by calling
[self.navigationController popViewControllerAnimated:YES];
While the subviews of the MainViewController get adjusted to Landscape according to the autoresize masks, willAnimateRotationToInterfaceOrientation: does not get called!
I would have expected willAnimateRotationToInterfaceOrientation: to be either also called when not on top of navigation controller stack or at least get called when returning to the top of the stack.
I assume I could manually call willAnimateRotationToInterfaceOrientation: before the pop but that feels wrong.
What am I missing?
This is expected behavior as I see it. If the UIVC is not on the top of the stack, then willAnimateRotationToInterfaceOrientation shouldn't be called as no rotation is being animated at that time. The way I've been handling this in the app I am working on now is similar to the above poster. Any UIVC that supports all orientations gets a new method
- (void) updateLayoutForNewOrientation: (UIInterfaceOrientation) orientation;
This method is called from two places:
-(void) viewWillAppear: (BOOL) animated {
[super viewWillAppear: animated];
[self updateLayoutForNewOrientation: self.interfaceOrientation];
}
-(void) willAnimateRotationToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation duration: (NSTimeInterval) duration {
[self updateLayoutForNewOrientation: interfaceOrientation];
}
The new method is simple:
- (void) updateLayoutForNewOrientation: (UIInterfaceOrientation) orientation {
if (UIInterfaceOrientationIsLandscape(orientation)) {
// Do some stuff
} else {
// Do some other stuff
}
}
Additionally, if you were worried about the code running when its not actually needed, you could track the orientation the device was in when the new UIVC was pushed on to the stack via an instance variable set in viewDidDisappear and consult it to decide if you want to "Do the stuff"
Isn’t this the correct behaviour? Because the animation callback should only be called before the animated orientation change. When you pop the topmost view, you see the pop animation and then the already rotated view underneath. There’s no rotation animation and therefore no callback. (But you should certainly receive willRotateToInterfaceOrientation:duration:.)
P.S. I have a sample Xcode project for interface orientation experiments on GitHub, you might be interested.
since there is no rotation taking place when you come back to the main view the willAnimateRotationToInterfaceOrientation will not get called. However, if you are looking to do some stuff based on the changed orientation, you could add the following code to your viewWillAppear method of the main viewcontroller
- (void)viewWillAppear:(BOOL)animated {
UIInterfaceOrientation statusBarOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if(UIInterfaceOrientationIsPortrait(statusBarOrientation)){
NSLog(#"Portrait");
....your code....
}
else {
NSLog(#"Landscape");
....your code....
}
}
I am with Scott on this one - I think it is the behaviour which has to be expected.
See also the View Controller Programming Guide where it states that only the frontmost controller will have the methods called. (Search for 'frontmost' to find the two relevant locations in the document.)
I have also experienced this behavior, and I think it's because of performance and architectural reasons; AFAIK this method is called inside a UIView animation context (which allows you to change frames and centers, and have a nice animation while everything rotates) and when your view controller is not visible, either UIKit avoids executing this method, or simply it's impossible to execute it, because the view is not visible (and probably you can't animate what you can see).
Again, this is pure speculation :)