I think I have been through probably 50 versions of this question today. The closest answer I got was putting
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow: (UIWindow *)window{
into my app delegate and
-(BOOL)shouldAutorotate
- (NSUInteger) supportedInterfaceOrientations
into the view controller I want to restrict to portrait mode. In this setup what I found is that the methods in my view controller never get called. The method in my appdelegate gets called whenever I do a tab bar based segue, but not when I do a push segue in a navigation controller.
I've seen several answers that want me to subclass navigation controllers, but there has to be a more straight forward way.
I have an app with three tabs. Tab 1 is just a home screen. Tab 2 has a navigation controller feeding through two tableviewscontrollers, and the last table view segue's into a simple view controller. Tab three goes to one tableviewcontroller which then does a push segue into the same simple view controller where tab 2 terminates.
I want that terminating view controller to always be in portrait. The other scenes should be able to switch between portrait and landscape as needed.
I am in xcode5 IOS7.
Thanks
The real answer to the question is that good apps fall into one of three categories: portrait-only apps, landscape-only apps, and apps that support both orientations in all view controllers.
The UX design goal: the user controls the app, the app does not control the user.
An app that has some view controllers that are portrait-only, and some view controllers that support rotation, is an app that is trying to control the user. Specifically, when the user navigates to the portrait-only view, the app is forcing the user to physically rotate the device in response to the app's whims.
In short, given that you have a view controller that only supports portrait, you should design a portrait-only app. If you don't want a portrait-only app, then you need to figure out how to support rotation on that last view controller.
Related
I'm trying to work with the iOS6 Auto-rotation mess.
I've looked at almost every single SO question relating to it, and no matter what I try, I can't get rotation working how I need it.
The app is using storyboards, and the layout is as follows:
Navigation controller ---> Root view controller ---> Tab view controller ---> View controller ---> Landscape view controller.
The view controller auto-rotates when I rotate the simulator, but when segueing back to the previous view (that is set to portrait), the view becomes landscape, when it should be portrait. If I rotate the simulator back, the view auto-rotates to portrait again, but this should've been done automatically!
I've implemented (UIInterfaceOrientation)preferredInterfaceOrientationForPresentationand it doesn't get called in any view controller I put it in.
I've subclassed the NavigationController to return the topViewController's shouldAutoRotate, supportedInterfaceOrientations and preferredInterfaceOrientationForPresentation and auto-rotation when rotating the simulator seems to work, but preferredInterfaceOrientationForPresentation never does its job.
Does anyone have a solution to this?
It would appear Apple have removed the ability to push a view in a specific orientation. preferredInterfaceOrientationForPresentation does get called, but only when popping back or presenting a view controller. I had to present my landscape view rather than push it, and set shouldAutoRotate = NO.
Refer to: In iOS6, trouble forcing ViewController to certain interfaceOrientation when pushed on stack for more details.
I have an application that plays videos via a MPMoviePlayerViewController that's presented after a selection action performed on a UITableViewController, which is embedded in a UINavigationController which is embedded in a UITabBarController.
TabBar Controller > Navigation Controller > Table View Controller * MPMoviePlayerViewController
Everything works as expected in iOS5, but upgrading to iOS6 I found that the video did not rotate as expected after being presented. If I selected more supported interface orientations on the target summary page, it causes the whole application to rotate.
The Apple documentation says the following in the UIViewController class reference:
In iOS 6, 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. Generally, 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 in 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.
But I'm not sure when the child view controllers are actually participating in the rotation decision. Will I need to change the way my Tab Bar Controller responds to shouldAutorotate and supportedInterfaceOrientations when a movie is playing?
The basic answer is that the rotation behavior of all View Controllers is determined by the "top most" view controller, but the MPMoviePlayerViewController determines its own rotation behavior since it acts as a "view controller presented to fill the entire screen".
For example: if I had a single-view application, the auto-rotate methods would be handled on the view controller for the single view. If I embed that view in a tab bar, then the tab bar controller implementation would handle those messages. If I embed the view in a navigation bar inside a tab bar, the tab bar implementation would still be the one handling the messages (it's still the "root view controller" if the other view controllers are embedded inside it).
MPMoviePlayerViewController will respond YES to shouldAutorotate and will support landscape orientations. It is still possible to prevent the movie from rotating (by not having portrait orientations selected on the target summary page), but the settings you choose for your view controller hierarchy will not affect its ability to do so. When the MPMoviePlayerViewController is presented, it is the view controller handling the autorotate messages. When it is not presented, the Tab Bar Controller is in charge.
I have a child UIViewController with that's part of a hierarchy with a UITabBarController and a UINavigationBarController. Let's call it ChildViewController; then my hierarchy looks like:
UITabBarController
|
UINavigationViewController [tab 1]
|
SomeParentViewController
|
SomeOtherParentViewController
|
ChildViewController
Now I want only ChildViewController to support rotation to landscape orientation. (It's a view controller that shows a chat view, and the landscape mode is easier for typing for some.) I added method - (BOOL) shouldAutorotateToInterfaceOrientation: to ChildViewController to declare that it supports landscape orientation, but rotating the device had no effect. From debugging, I found that – willAnimateRotationToInterfaceOrientation:duration: wasn't being called.
After some searching around online, I've found that a descendent of a UITabBarController only supports a given orientation if the UITabBarController itself supports that orientation. And, strangely enough, UITabBarController only supports an orientation if the view controllers for each of its tabs support rotation. Like tab 1 above, the view controllers for the other three tabs are UINavigationViewController instances; and, because we must go deeper, each UINavigationViewController only supports orientation if its child view controller supports the orientation.
So at this point, adding adding - (BOOL) shouldAutorotateToInterfaceOrientation: to SomeParentViewController and the children of the other UINavigationController instances allowed ChildViewController to rotate. But now SomeParentViewController and the other three tabs will rotate to landscape, and it looks horrible. I only wanted ChildViewController to support landscape.
As a latch ditch effort, I created my own UITabBarController subclass called RotatingUITabBarController and add a global flag to the ChildViewController class that lets me know if it has been created and is displayed. The RotatingUITabBarController overrides only - (BOOL) shouldAutorotateToInterfaceOrientation: and is implemented as:
if ([ChildViewController isDisplayed]) {
return ((toInterfaceOrientation == UIInterfaceOrientationPortrait) ||
(toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
(toInterfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
return NO;
Now, if I boot the app, switching to SomeParentViewController or any other tab and rotating the phone does not switch to landscape mode, instead keeping in portrait. So far so good. If I create and display ChildViewController and rotate the phone, it enters landscape. So far so good. But now if I pop ChildViewController to reveal SomeOtherParentViewController, it is also in landscape. And so is SomeParentViewController and every other tab that I switch to.
I'm out of tricks now. Any advice would be much appreciated, thanks!
Perhaps the best model for the kind of behavior you seem to want is the YouTube app. Most the interface is portrait-only, but the view that plays videos works in either portrait or landscape.
If you look at that app, you'll notice that the whole tabbed part of the UI is actually a modal view controller. When you launch the app, the tab bar controller is immediately presented modally. The only time you leave that modal tab bar controller is when you play a video -- you'll notice that the whole tabbed interface slides down to reveal the video view. When the video ends, the tab bar controller is again presented modally.
This is an inversion of the "normal" approach, where you use a modal view controller only briefly, but it works very well in the YouTube app. It may or may not work well for you too. The important thing is to make your app predictable and fluid, and make the user feel in control at all times.
I have a UITabViewController application with 4 tabs and each tab with a UINavigationController, each with a UItableView. When a row is clicked it navigates to another view.
I would like to support Landscape Orientation only for a certain UIViewController and not in any other view.
When I set "return YES" on each UINavigationcontrollers (BOOL)shouldAutorotateToInterfaceOrientation: method the app orients in all the views even in the uitableview.
How do I get this right? I am very confused
If you want to maintain the views as per your requirement then you have to make different view for each tab also in Interface builder you have to create landscape view wherever you required. When you add that landscape view, which is created in interface builder will be shown as landscape only. There will be no need of shouldAutorotateInterfaceOrientation method.
I have a UITabBar/UINavigation application and I'm having some trouble allowing autorotation in a given view.
The TabBar allows changing sections, with table view items. When one of the items is tapped, I push a new view which hides the TabBar and which should autorotate. I tried the easy way, which seemed most logical to me: disable autorotate in the rootViewController and allow in the detailViewController, but this didn't work (shouldAutorotateToInterfaceOrientation returns YES, but then willRotateToInterfaceOrientation is never called and view doesn't autorotate). I read that all VCs in a TabBar should return YES to shouldAutorotateToInterfaceOrientation, so I did that, but the result is that now my whole application rotates.
I then subclassed my UINavigationController and set shouldAutorotate to NO, hoping that I could detect when the view that was being shown was in fact a detailView, and then return YES... I can't seem to do that.
Any help out there?
Thanks!
Antonio
It sounds like you've got a set up like the iPod app which has a tabbar for playlist view, songs view etc but which disappears when you go to a detail view for a song. The detail view can rotate but the tabbar views do not. When you do rotate the tabbar it turns into a cover flow detail view.
I'm pretty sure they do this by putting the tabbar inside a navigation controller. When you go to the detail view, it pops the tabbar entirely and pushes the detail view.
So the actual hierarchy looks something like:
Nav {
tabbar {
playlist
Artist
//... other tabs
}
detail view portrait
detail view cover flow
}
Only one of the sibling views (tabbar, detail portrait, detail coverflow) is pushed at any one time.
The iPod app does this because the detail view is the primary functional view for the entire app so the rest of the app is built around navigating to it. If that is not the case for your app, then this setup may be more trouble than it is worth.