Forcing UIInterfaceOrientation changes on iPhone - iphone

I'm strugging with getting an iPhone application which requires just about every push or pop in the Nav Controller Stack to change orientation.
Basically the first view is portrait, the second landscape the third portrait again (Yes I know this is less than ideal, but that's the design and I've got to implement it).
I've been through various advice on here....
How do I detect a rotation on the iPhone without the device autorotating?
Force portrait orientation on pushing new view to UINavigationViewController
Is there a documented way to set the iPhone orientation?
But without total success.
Setting to link against 3.1.2 my reading of the linked articles above seems to indicate that if my portrait view pushes a view with
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return ((interfaceOrientation == UIInterfaceOrientationLandscapeRight) );
}
Then then that view should appear rotated to landscape. What happens is it appears in its "broken" portrait form, then rotates correctly as the device is turned.
If I pop the controller back to my portrait view (which has an appropriate shouldAutoRotate...) then that remains in broken landscape view until the device is returned to portrait orientation.
I've also tried removing all the shouldautorotate messages, and instead forcing rotation by transforming the view. This kind of works, and I've figured out that by moving the status bar (which is actually hidden in my application) [UIApplication sharedApplication].statusBarOrientation = UIInterfaceOrientationLandscapeRight; the keyboard will appear with the correct orientation when desired.
The problem with this approach is that the status bar transform is weird and ugly when you don't have a status bar - a shadow looms over the page with each change.
So. What am I missing.
1) Am I wrong in thinking that in 3.1.2 (or possibly earlier) shouldAutorotateToInterfaceOrientation should provide the desired orientation simply by pushing controllers ?
2) Is there another way of getting keyboards to appear in the correct orientation.
3) Are the undocumented API calls the way to go (please no!)

You shouldn't use [UIViewController shouldAutorotateToInterfaceOrientation:] to trigger an orientation change; it's only there to let the system know if automatic rotations are allowed. You should still update it to specify the orientation that's allowed though.
If you want to change the orientation when a particular view is showing, you should call [UIApplication setStatusBarOrientation:animated:] inside your [UIViewController viewWillAppear:] override method for each of the view controllers that force a particular orientation. That will cause a change when a view is being pushed onto the stack and when it's being popped off it. Make sure you call super in your override method.
This is also the right place to change how the status bar is displayed, if that's something you're doing.

Related

Pushing portrait view controller onto landscape view controller?

I am trying to push a portrait only view controller onto a controller that allows portrait or landscape. The issue I'm having is that if the user is in landscape mode and I push the new controller on it will remain in landscape mode and just look all screwed up. How do I force the orientation to change to portrait as the new view controller is pushed on?
KDaker's answer is correct but another option you can think about is whether you can limit navigation when your orientation isn't what you want. This isn't necessarily a good idea but there are situations where it can work well. An example would be if you had a video which when rotated to landscape became full screen and covered your navigation back button until it returns to portrait.
in iOS 5 and anything before you cant 'force' an orientation from one view to another. You can support only one orientation for the project but then allow autorotation. So in your case, you can only allow autorotation and wait for the user to rotate the device.
In iOS 6, they have changed the way orientation handling works, and its become alot more flexible. You now have these methods:
- (NSUInteger)supportedInterfaceOrientations
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
Using these, you can present your view controller in any orientation you prefer, given that it is supported in the former method.
hope this helps.

Why doesn't my view display in Landscape instead of Portrait mode?

I use to define my UIViews programmatically, setting the orientation via code. I haven't touched Interface Builder in a while and I've decided to go for the new Storyboarding system.
I was pretty happy until I found out that although I have set, in the inspector pane, the appropriate view controller to "Landscape", it never displays in another mode than portrait.
I commented in and out the code in my custom view controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscape);
}
Nothing changes.
My View Controller is itself "under" a Navigation Controller on the Storyboard, so I suspect interference, and it's only the second view in the flow, so the fact that the application itself is defined as portrait should not interfere.
I am looking for ideas to test for at this point, since the application is stripped to so little code I really can't begin to guess where to look?
What supported orientations have you specified in Info.plist? In order for a UINavigationController to support rotation, all of it's child view controllers must also support rotation to the same orientation.

Separate interface orientations per view

I am working on an iPhone app with a UINavigationController interface and there I want all views to ONLY allow default portrait rotation except for one. I know I can return 'NO' in the shouldAutoRotate method but basically when I am in the view that does allow rotation and go back to the previous view, the other views are then stuck in landscape as well. Any ideas?
you need to change status bar orientation when you go back to previous view.

Alert views in portrait, iPad in landscape, and unable to rotate to anything but "other" landscape (180 degrees)

Update: Solved. See fix at bottom.
In my iPad app which supports all orientations, if the app is loaded in landscape, weird things happen:
Alert views appear in portrait, even though the rest is in landscape
Rotating the iPad from landscape to portrait does not trigger a rotation (nor does it trigger any of the shouldAutorotate style methods).
Rotating the iPad 180 degrees (to "other landscape") does trigger rotation, and after this, the iPad behaves like normal (i.e. rotates correctly to portrait and landscape when rotated).
Alert view screen below. Weird, weird. Any ideas why this might be happening? It only happens when loading in landscape -- loading in portrait and then rotating to landscape works just fine.
Solution: This problem occurred because I was adding a UIViewController (modally) on top of the main view controller as soon as the app finished loading (more specifically, in the app's viewDidAppear: method).
iOS sends a number of (4) shouldAutorotateToInterfaceOrientation: calls to the current/main view controller after things have started up, to figure out which orientations are currently supported. When I added the other VC so soon, this occurred in the middle of the aforementioned calls. Thus, I only got one or two calls (for "portrait", because it starts with portrait).
The main view controller was then left with a default support of "NO" for the landscape orientations, but the newly added view controller (which is also asked about orientations) now happily responds yes.
If I had removed the other VC (which is in landscape), I would have realized that my main view controller was stuck in portrait. So I had a portrait view, on top of which there was a landscape view. The alerts and such heeded the orientation of the former, hence the disastrous results above.
Additionally, if I tried to rotate to portrait, I couldn't, because the main view controller was already in portrait. Rotating to landscape was also impossible because the other view controller is already in landscape. Only when I did the "landscape but that other one" rotation, did both view controllers agree that rotation was required, and thus the device rotated.
The fix in my case was to simply put the creation of the other controller at a slight delay. using performSelector:withObject:afterDelay. -- 0 seemed to work but I just put it at 1 to be sure. It looks a bit weird, with the main vc appearing and then the other vc, but it's better than the above.
This problem occurred because I was adding a UIViewController (modally) on top of the main view controller as soon as the app finished loading (more specifically, in the app's viewDidAppear: method).
See above post for details on solution.
Use UIAlertController instead of UIAlertview.

Selective Autorotation of UIControllers

I did some experiments on autorotation.
The situation: I have a TabBar 4(tabs), three should be portrait only. The last one is a UINavigationController, which by itself should not autorotate any of the stacked controllers. It is basically a browsing application, as I show file and folders everything should be portrait. Some times, a special UIViewController is pushed, and I would like only this one to autorotate (it is always the last on the stack). In this last view, the tabbar is hidden.
How I achieved the goal: I subclassed the UITabBarController, to override the standard shouldAutorotate method behaviour:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if([self.selectedViewController isKindOfClass:[UINavigationController class]])
return [[(UINavigationController*)self.selectedViewController visibleViewController] shouldAutorotateToInterfaceOrientation:interfaceOrientation];
else
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
This way, the answer of shouldAutorotate is forwarded to the controlled tabs, and in particular for the UINavigationController, it is again delegated to the visible UIViewController. Basically this works, as I have all the UIViewControllers answering NO, except for the particular one I described above: correctly, when rotating the Simulator, only when the special UIViewController is visible, the interface rotates to landscape, whici is perfect. The Tabbar here is hidden, so user don't get that also that one is rotated (which would be unconsistent in my design: basically whenever the tabbar is visible, which means everywhere except in this special view, the application is portrait only).
The problem is that I would like that, even if the device is still in landscape mode and user pops the special ViewController, the interface should behave consistently and return to portrait mode. Instead, when I pop, the interface stays in landscape (it's not designed in that way so it's a mess, of course) even when showing a UIViewController that would answer NO to shouldAutorotate... this is because (I think) the method is called only when rotation occurs, so until the rotation actually occurs again, the interface is rotated to landscape anyway.
How do avoid this? My first solution would be somehow to intercept the popping of the last view, and rotate manually the view before popping... but I'm not sure, I hope there is some more robust method to handle!!
I use the simulator with 3.0, dunno if this makes a difference.
I know that this is not a solution to your problem, but I think you should really avoid this kind of user interface when portrait-only portrait+landscape or landscape-only pages mixed on the same UINavigationController. Unfortunately the rotation management is extremely buggy and the bugs vary on different firmware versions.
I managed to quite the same thing in one of my projects, but had to remove it later due to firmware bugs: for example if you pressed the "back" button in landscape mode and went back to a portrait-only view, it often occured that the status bar and/or the navigation bar remained in landscape mode and the layout was completely broken. As far as I know this bug is not yet fixed although it was already present in firmware 2.x.
If you still want to do this I suggest the following things:
Make sure that all overridden UIViewController methods (init, viewWillAppear, etc) calls its [super methodName]. If not, auto-rotation is silently buggy. This was mentioned in the "Getting Ready for iPhone OS 3.0 Technical Note" (https://developer.apple.com/iphone/checklist/), but currently this document is unavailable :(
You may experiment with calling the undocumented [UIDevice setOrientation:] method when leaving the landscape view. It sometimes needs to be called twice, once with the current orientation and once with the desired orientation :) You may also need to call [UIDevice setStatusBarOrientation:] if the status bar remains in landscape mode. But note that Apple is likely to reject your application if you use these methods (they introduced an automatic tool some time ago which detects the presence of undocumented symbols in your application).
I had the same problem as you, and I solved this way:
I subclassed the UITabBarController, and added the following code:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (self.selectedViewController)
return [self.selectedViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
else
return (interfaceOrientation == UIDeviceOrientationPortrait);
}
This way, every child view controller could control its own orientation.
The problem is that you are rotating your UITabBarController (with the child view controller on top of it) rather than just the child view controller. You should be able to implement shouldAutorotateToInterfaceOrientation: only in your child view controller and have it work properly. It would also simplify your code.
I have never had this issue, but I've also never implemented shouldAutorotateToInterfaceOrientation: in a "container" view controller like a UITabBarController or UINavigationController.
The problem with your implementation is that you use the visibleViewController member of UINavigationController. You should use topViewController instead and everything will work as expected.