I am making an universal app, but only support rotation on iPad so here it goes:
I have a view that has a image as background. When the user rotates the iPad I fade this portrait image out in the willRotateToInterfaceOrientation and replaces the image with a landscape image in didRotateFromInterfaceOrientation, and this is working great! (If there are better ways I want to hear them, but this question is not about this though).
I have one button on this view that will push a new viewcontroller on the stack. This view controller also has the same "function" as the view that it was pushed from. What I need help with is: When I rotates the iPad from portrait (which was the orientation this view was pushed on from) to landscape the image in this "second" view changes as intended. However when I pop this viewcontroller off stack the "first" view still displays the image which is made for portrait and not the landscape one...I understand why but not how I should fix this.
Could anyone help me. Thank you for your time.
You can save the device orientation (check UIDevice documentation) on your viewWillDisappear: and check it on the viewWillAppear: method, calling the animation if necessary.
(the OP told me to post it as answer)
You could always register for UIDeviceOrientationDidChangeNotification and change the image if the controller is not on the top stack.
Assuming you're using a UINavigationController (since you mentioned pushing controllers), you could figure out if the controller is on top by:
if ([[self navigationController] topViewController] == self) {
// do magic
}
Use the first view controller's viewWillAppear method. In there, you can find the device's orientation by calling [[UIDevice currentDevice] orientation] and set the background image accordingly.
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 a Navigation Controller and TabBar. I would like to know how the iPod app handles allowing only the play screen to rotate. I have tried to replicate this on iOS 4.2, and if that one screen rotates to landscape and you navigate back, the old screen is too. This is undesired... is there anyway to make the 2nd screen in rotate while not causing the rest of the screens or any of the TabBars view controllers elsewhere do so?
Thanks.
Make sure your rotatable view returns YES in shouldAutorotateToInterfaceOrientation: method of UIViewController.
Make sure your non rotatable view returns NO in shouldAutorotateToInterfaceOrientation: method of UIViewController.
Subclass UITabBarController, override shouldAutorotateToInterfaceOrientation: and handle rotation here based on current UIViewController's shouldAutorotateToInterfaceOrientation: return value. You have to also check if current view controller is UINavigationController or not and if yes, you have to get current view controller from UINavigationController too.
It's not recommended to do this (I mean subclassing of UITabBarController), but UITabBarController forbids rotation if not all UIViewControllers do allow rotation.
You need to state that in the 2nd screen the device orientation is only the desired screen orientation. So for every controller you create you need to implement shouldautorotatetointerfaceorientation:.
I have an application which is focused around a bunch of viewControllers in portraitmode, but on a specific detail view i need to open another view if the device is rotated to landscape mode.
So the user will look at the information view in portraitmode and if the user then rotates the device to landscapemode then a new view is displayed with additional information. If the user rotates back to portrait then the added view needs to be removed so the "original" detailview is visible.
It's important that the "original" detailview is not rotated to landscape - Only open a new view in landscape mode.
I've tried using shouldAutorotateToInterfaceOrientation: and managed to have it open a viewController, but it's not being shown in landscape view so it looks all messed up plus I'm having some trouble getting the view to disappear when i rotate back to portraitmode.
How do i do this?
Check if the orientation has changed using the View controllers did change orientation methods and if its rotated to landscape add ur landscape view and when the device is rotated to portrait remove the view from the view controller's view.
in shouldAutorotateToInterfaceOrientation
if(UIInterfaceOrientation == UIInterfaceOrientationPortrait){
NewView *newViewController = [[NewView alloc]initWithNib:#"NewView" bundle:nil];
[self.navigationController pushViewController:newViewController animated:NO];
}
you can repeat this for all the other orientations as well.
Using shouldAutoRotate didn't work since the view that gets opened will be opened in portraitmode and not landscape.
I ended up with a solution using beginGeneratingDeviceOrientationNotifications and shouldAutoRotate in the subview.
I have an application that consists of a login, 3 tables, and then an image.
You can rotate the image to landscape mode but what I want to be able to do is when the 'back' button is pushed and the app returns to the previous screen, I was the app to automatically rotate to give a portrait view.
Is there any way of doing this?
For previous controller:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
return YES;
}
return NO;}
If the previous view controller only supports portrait (see shouldAutorotateToInterfaceOrientation:) then it should automatically rotate.
If the previous view controller supports landscape but you want it to rotate to portrait if it was originally in portrait, you can probably force it by changing what shouldAutorotateToInterfaceOrientation: returns when navigating back. I wouldn't recommend this; it's inconsistent UI (and it's a bit tedious to figure out what navigation is going on).
There are various ways to set the view controller interface orientation (-[UIDevice setOrientation:] will attempt to trigger an autorotation), but then you're into the realm of private APIs and potential rejection.
Override shouldAutorotateToInterfaceOrientation: in your table view controller and only return the orientation you want.
This might not actually switch back the orientation, so if this alone doesn't work you might have to call setStatusBarOrientation:animated: on UIApplication in your table view controller's viewWillAppear: or viewDidAppear: methods.
There has been a lot of confusion and a set of corresponding set of questions here on SO how iPhone applications with proper handling for Landscape/Portrait mode autorotation can be implemented. It is especially difficult to implement such an application when starting in landscape mode is desired. The most common observed effect are scrambled layouts and areas of the screen where touches are no longer recognized.
A simple search for questions tagged iphone and landscape reveals these issues, which occur under certain scenarios:
Landscape only iPhone app with multiple nibs:
App started in Landscape mode, view from first nib is rendered fine, everything view loaded from a different nib is not displayed correctly.
Iphone Landscape mode switching to Portraite mode on loading new controller:
Self explanatory
iPhone: In landscape-only, after first addSubview, UITableViewController doesn’t rotate properly: Same issue as above.
iPhone Landscape-Only Utility-Template Application: Layout errors, controller does not seem to recognize the view should be rotated but displays a clipped portrait view in landscape mode, causing half of the screen to stay blank.
presentModalViewController in landscape after portrait viewController: Modal views are not correctly rendered either.
A set of different solutions have been presented, some of them including completely custom animation via CoreGraphics, while others build on the observation that the first view controller loaded from the main nib is always displayed correct.
I have spent a significant amount of time investigating this issue and finally found a solution that is not only a partial solution but should work under all these circumstances. It is my intend with this CW post to provide sort of a FAQ for others having issues with UIViewControllers in Landscape mode.
Please provide feedback and help improve the quality of this Post by incorporating any related observations. Feel free to edit and post other/better answers if you know of any.
What's in the documentation:
In your view controller, override shouldAutorotateToInterfaceOrientation: to declare your supported interface orientations. This property will/should be checked by the controller infrastructure everytime the device orientation changes.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
{
return (orientation == UIInterfaceOrientationLandscapeRight);
}
This is the absolute minimum your view controller needs to do. If you want to launch your application in landscape mode, you need to add the following key to your .plist file:
<key>UIInterfaceOrientation</key>
<string>UIInterfaceOrientationLandscapeRight</string>
Apple recommends starting landscape only applications in Landscape Right mode (see the HIG under User Experience Guidelines > Start Instantly).
What's not in the documentation:
A little background:
Everytime you try to load a different view controller other than that loaded from the main nib, your view controller is neither interrogated about it's supported interface orientations nor is its frame set correctly. Only the first view controller bound to the window will be layed out correctly.
Other people have suggested using a "MasterViewController" hooked up to the main window to which other controllers add their views as subviews instead of hooking directly into the window. While I have found this solutions is a viable option, it does not work correctly in the case of modal view controllers added to those said subviews. There's also a problem if you have some subviews that should be able to autorotate (what the master controller will prevent).
The usage of undocumented API's to force a certain interface orientation is not an option either.
The solution:
The best solution I have found so far is a modification of the "MasterViewController" workaround. Instead of using a custom "MasterViewController", a UINavigationController with hidden Navigation Bar and hidden Tab Bar is used. If all other views are pushed/popped from the navigation stack of this controller, auto-rotations of controllers on that stack will be managed correctly.
Modal controllers presented via presentModalViewController:animated: from any of the view controllers on the UINavigationController's navigation stack will be rotated and rendered with correct layout. If you want your modal view controller to be rotatable to a different orientation than that of the parent view controller, you need to return the desired orientation from the shouldAutorotateToInterfaceOrientation method of the parent controller while the modal view is presented. In order to properly restore the interface orientation when the modal controller is dismissed, you need to make sure shouldAutorotateToInterfaceOrientation returns the desired orientation for the parent controller before you call dismissModalViewController:animated:. You can use a private BOOL on your view controller to manage that (e.g. BOOL isModalMailControllerActive_).
I'll add a piece of sample code soon, It's just to late now. Please let me know if any unresolved issues remain or anything is unclear about this post. Feel free to edit and improve.
I had an interesting requirement for ios application:
main viewController should be only landscape, but all others (which can be pushed from main) can be landscape and portrait.
Problem occours - when I push to a new viewController, which then is rotated to portraited - and the pop back - main view is no longer landscape. Also - opening application, it is not in landscape.
In order to keep main viewcontroller landscape, no matter from what orientation it was popped/pushed, I did the following thing: (in viewWillAppear:)
//set statusbar to the desired rotation position
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft animated:NO];
//present/dismiss viewcontroller in order to activate rotating.
UIViewController *mVC = [[[UIViewController alloc] init] autorelease];
[self presentModalViewController:mVC animated:NO];
[self dismissModalViewControllerAnimated:NO];
Hopefully it will help someone!
P.S.Tested on sdk 3.2.5 ios 5.0.1.
P.S. Thanks for all the info in this FAQ!
For the second bullet point, if you want to use pushViewController to go from Portrait-only to Landscape-only view, one simple hack I found is to put the following code into your pushed controller's viewDidLoad:
UIViewController *viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
[viewController release];
I'd like to add to Johannes' answer (using the UINavigationController as MasterViewController).
The disadvantage I have found is that ViewControllers that are newly pushed onto the master VC's navigation stack do not adjust to any prior orientation changes. In short, VCs already on the stack are rotated, modal view controllers presented from them are also rotated, but newly added VCs are not rotated.
I have tried many tricks to fix this before finding one that works. Most only work for pushViewController: with animated set to YES.
To fix the issue entirely, I have subclassed UINagivationController and overriden pushViewController:animated: as follows:
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
// Correctly autoOrient the given view controller
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
// Push it as normal (now in its correct orientation)
[super pushViewController:viewController animated:animated];
}
Temporarily (and quite invisibly) presenting the view controller allows it to receive its orientation update. It can then be pushed onto the navigation stack in its correct orientation.
Please let me know if this works for you. All updates and improvements are very welcome!
Finally, I highly recommend Johannes' approach to rotation management.
EDIT: Update on popping view controllers from the stack
It seems that all the popViewController-related selectors go crazy when performed with animated:YES. Specifically, the view controller is animated out in the wrong direction. You can use animated:NO and restrict the use of such animations to other UINavigationControllers deeper down in your hierarchy (i.e. the ones that you push onto the root navigation controller's stack).
Any input is greatly appreciated.
I'm developing an iPad app that displays vertically scrolling gallery view of an array of items upon startup. In landscape mode there are 4 items across. In portrait there are three. When turning the iPad orientation it is supposed to refresh the gallery to have the items fit neatly across the screen. Then I double tap on an item to drill down to a modal view of that item. Then I do stuff with that item. Finally I dismiss the modal view.
During the refresh or orientation change the gallery view calculates the number of items to display based on the screen width (or height) and the current orientation from UIViewController.interfaceOrientation.
I was having a problem getting this to work correctly. Sometimes it would display only two items in landscape orientation after I dismissed the modal dialog.
At first I was using the UIViewController.view.frame.size values to calculate the number of gallery items. When the modal view was dismissed this frame size was incorrect e.g. the width and height had been reversed even though the orientation had not changed while the modal dialog was displayed.
I switched to using the application delegate ([[UIApplication sharedApplication] delegate]] and taking the window.frame.size to calculate the number of gallery items to display across. The window.frame.size stays correct under orientation changes and modal dialogs. The gallery items display correctly now.
This will work...
UIWindow *window = [[UIApplication sharedApplication] keyWindow];
UIView *view = [window.subviews objectAtIndex:0];
[view removeFromSuperview];
[window addSubview:view];