The following are the two layouts that I want have in my application. It would be nice if when the application switches from portrait to landscape that it keeps the UILabels, BOOLs, and other objects. Because the buttons are situated differently I cannot just have the portrait view autoresize on an auto rotate. I also want to implement my own rotation lock using a BOOL and the button on the top right.
I thought about using a -(void)orientationChanged:(NSNotification )notification with a presentModalViewController however these didn't copy over the objects and seemed to cause more harm then good and didn't appear to work properly.
Thanks for the help!
Attempted Solutions:
I added the landscape view to the ViewController, having both views in the view controller. I linked it up to File's Owner under the UIView *landscapeView that I added in the #interface section of the ViewController. I added [self.view addSubview:landscapeView] to the viewDidLoad method. Then I added this piece of code:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if (orientationLock)
return NO;
else {
if (interfaceOrientation == UIInterfaceOrientationPortrait) {
[landscapeView setHidden:YES];
//[self.view setHidden:NO]; removed
} else {
//[self.view setHidden:YES]; removed
[landscapeView setHidden:NO];
}
return YES;
}
}
However this is not the correct solution. When I run the simulator and rotate it, the screen is not properly placed.
All things(your instance variables values) remains same when the device changes orientation. If you have only one viewController and you are showing both orieantation in the same then you can easily manage this. I suggest you to create two UIViews in the nib file that way you can do all the things you want.Hope you understand what I am talking about.Let me know if you need help.
Here is modified code
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
if (orientationLock)
return NO;
else {
if (interfaceOrientation == UIInterfaceOrientationPortrait) {
[landscapeView setHidden:YES];
} else {
[landscapeView setHidden:NO];
}
return YES;
}
}
Related
My app mainly supports iOS6+. When considering about iOS5, I added the following judgement.
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) {
self.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:readerViewController animated:YES completion:NULL];
}
else {
self.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentModalViewController:readerViewController animated:YES];
}
However, the modal view presents vertically in my landscape app. I mean, my app is landscape, the modal view just "lies" there, not fullscreen as I set, just cover part of screen while the uncovered is black.
I wonder if anyone could help. Thanks in forward.
Lilac :- What tia said is correct. you dont need to worry about the version.I think you haven't set the Orientation for the ModalView with the required view Oreintation.
Means, if your mainViewController (from which you are presenting ModalView) is supporting only Landscape Mode then in the modalViewController, you have to set the Orientations, restricting to present in Landscape View only.
You should write the Orientation code written in mainViewController also in the modalViewController.
these Methods :-
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight || interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
// return YES;
}
-(BOOL)shouldAutorotate{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscape;
}
Kindly revert if it does not solve your problem and if it does help, you know what to do ;).
First, presentViewController:animated:completion: has been available since iOS 5.0 so you do not have to worry about the version. In case if you really want to check the availability of the method, you should use
if ([self respondToSelector:#selector(presentViewController:animated:completion:)])
For the modal view, you need to set modalTransitionStyle and modalPresentationStyle on presented controller e.g. readerViewController in your case. So your code should be
readerViewController.modalTransitionStyle = UIModalPresentationFullScreen;
readerViewController.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentModalViewController:readerViewController animated:YES completion:NULL];
Having read every method of handling screen rotation in IOS 6 on here, Apple documents, and fierce Googling, after days and days I am still stuck on this when using xibs.
What im trying to achieve is have a few iPad only xibs, in landscape only, rest of app is portrait.
If I was using storyboards I understand how to subclass the nav controller to override the rotation but I am NOT, im using xibs and would appreciate any input how this works with xibs
In my app delegate:
ICHomeController *homeController = [[ICHomeController alloc] init];
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:homeController];
navController.navigationBarHidden = YES;
navController.navigationBar.barStyle = UIBarStyleBlackOpaque;
navController.navigationBar.tintColor = [UIColor grayColor];
self.navigationController = navController;
[self.window setRootViewController:self.navigationController];
[self.window makeKeyAndVisible];
return YES;
In addition to trying what seems like every solution on here Ive tried the obvious in my vew controllers, but as I understand in iOS 6 it doesn't get called.
// Older versions of iOS (deprecated)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return UIInterfaceOrientationIsLandscape(toInterfaceOrientation);
}
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeLeft;
}
I've been battling with this for days too with a more complex navigation structure.
The basic rule seems to be that the ROOT controller, whether it be a UINavigationController, UITabBarController or just a simple UIViewController, takes control of the rotation. So as you PUSH UIViewControllers onto the stack, the very first one still controls the rotation.
If you use a modal view, then this model view will be considered the ROOT of the stack until it is dismissed. So if you show a modal UIViewController then push views on top of it, the UIViewController that was presented as the modal view is in control of the rotation.
Just make sure for modal views to use presentViewController:animated:completion.
The easiest way to get certain views to be in a different rotation, is present it modally.
If this can't be avoided, then go right back to your first view or nag controller and set the rotations.
Add some NSLog(#"%s", __FUNCTION__); statements to your rotation methods to see which ones are being called.
In your code above, only the first method is the old iOS5 (Now deprecated) method. The other 3 are the new iOS 6 methods.
Using your example code above, you would need to subclass the UINavigationController. To do this create a new class as a UINavigationController subclass with a different name eg. MyNavigationController and add the rotation methods to it. Then in your code above instead of using
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:homeController];
You would use:
MyNavigationController *navController = [[MyNavigationController alloc] initWithRootViewController:homeController];
Also, make sure you have allowed orientations selected here:
I had the same problem. Here's what worked for me in 5 and 6.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft || toInterfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
- (BOOL)shouldAutorotate NS_AVAILABLE_IOS(6_0)
{
return NO;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation NS_AVAILABLE_IOS(6_0) {
return UIInterfaceOrientationLandscapeLeft;
}
In terms of the target summary, only portrait mode is "supported".
I want to rotate ONLY one of my views within my app to either landscape left or landscape right. All my other views are in portrait mode and I have set my app to support only portrait mode. With orientation being changed in iOS 6, I am not sure how to do this. I have tried the following posted below. Can anyone tell me what I am doing wrong? Thanks!
-(BOOL)shouldAutorotate {
return YES;
}
-(NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight;
}
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft;
}
I have also tried:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(didRotate:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
return YES;//UIInterfaceOrientationMaskLandscapeRight | UIInterfaceOrientationMaskLandscapeLeft;
}
-(void)didRotate:(NSNotification *)notification {
UIDeviceOrientation orientation = [[notification object] orientation];
if (orientation == UIDeviceOrientationLandscapeLeft) {
[theImage setTransform:CGAffineTransformMakeRotation(M_PI / -2.0)];
[self.view setTransform:CGAffineTransformMakeRotation(M_PI / 2.0)];
} else if (orientation == UIDeviceOrientationLandscapeRight) {
[theImage setTransform:CGAffineTransformMakeRotation(M_PI / -2.0)];
[self.view setTransform:CGAffineTransformMakeRotation(M_PI / -2.0)];
} else if (orientation == UIDeviceOrientationPortraitUpsideDown) {
[theImage setTransform:CGAffineTransformMakeRotation(M_PI / -2.0)];
[self.view setTransform:CGAffineTransformMakeRotation(M_PI / -2.0)];
} else if (orientation == UIDeviceOrientationPortrait) {
[theImage setTransform:CGAffineTransformMakeRotation(M_PI / 2.0)];
[self.view setTransform:CGAffineTransformMakeRotation(M_PI / 2.0)];
}
}
This worked for me How to force a UIViewController to Portrait orientation in iOS 6
Create a new category from UINavigationController overriding the rotating methods:
-(BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
-(NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return [self.topViewController preferredInterfaceOrientationForPresentation];
}
#end
There are changes in iOS 6 regarding handling view rotations. Only orientations defined in apps Info.plist are supported. Even if you are returning other ones.
Try to select all orientations as supported in your project.
Handling View Rotations
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.
You can override the preferredInterfaceOrientationForPresentation for a view controller that is intended to be presented full screen in a specific orientation.
In iOS 5 and earlier, the UIViewController class displays views in portrait mode only. To support additional orientations, you must override the shouldAutorotateToInterfaceOrientation: method and return YES for any orientations your subclass supports. If the autoresizing properties of your views are configured correctly, that may be all you have to do. However, the UIViewController class provides additional hooks for you to implement additional behaviors as needed. Generally, if your view controller is intended to be used as a child view controller, it should support all interface orientations.
When a rotation occurs for a visible view controller, the willRotateToInterfaceOrientation:duration:, willAnimateRotationToInterfaceOrientation:duration:, and didRotateFromInterfaceOrientation: methods are called during the rotation. The viewWillLayoutSubviews method is also called after the view is resized and positioned by its parent. If a view controller is not visible when an orientation change occurs, then the rotation methods are never called. However, the viewWillLayoutSubviews method is called when the view becomes visible. Your implementation of this method can call the statusBarOrientation method to determine the device orientation.
(C) Apple Docs: UIViewController
Follow the below steps
Create subclass of UINavigationController overriding the rotating methods.
In AppDelegate, create a BOOL islandscape property.
When a view is pushed/poped/present/dismiss, adjust this BOOL value.
Sample Project
I created a sample project for this which is working perfectly. Download and integrate in your project: https://www.dropbox.com/s/nl1wicbx52veq41/RotationDmeo.zip?dl=0
I have a:
TabbarController -> NavigationController -> ViewController -> ViewController
I Subclassed UITabBarController and add....
-(NSUInteger)supportedInterfaceOrientations{
if (self.selectedIndex >= 0 && self.selectedIndex < 100) {
for (id vC in [[self.viewControllers objectAtIndex:(unsigned long)self.selectedIndex] viewControllers]) {
if ([vC isKindOfClass:[CLASS_WHICH_SHOULD_ALLOW class]]) {
return UIInterfaceOrientationMaskPortrait + UIInterfaceOrientationMaskLandscape;
}
}
}
return UIInterfaceOrientationMaskPortrait;
}
I have been searching for the solution for hours!
So after implementing the needed methods everywhere. shouldAutorotate doesn't need to be set to YES because it is already set as default:
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationPortrait;
}
When it is time to show the UIViewController which needs the orientation different than the other views, I created a UIStoryboardSegue with this implementation inside:
#import "Showing.h"
#implementation Showing
- (void)perform{
NSLog(#"Showing");
UIViewController *sourceVC = self.sourceViewController;
UIViewController *presentingVC = self.destinationViewController;
[sourceVC.navigationController presentViewController:presentingVC
animated:YES
completion:nil];
}
#end
Inside the UIStoryboard I connected the views with this segue (showing):
It is just important, you are using
presentViewController:animated:completion:
AND NOT
pushViewController:animated:
otherwise the orientation won't be determined again.
I had been trying things like
[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
OR this one inside the UIViewController where the orientation should change, and I also tryied to call it inside my custom UIStoryboardSegues before presentingViewController and dismissViewController:
[UIViewController attemptRotationToDeviceOrientation];
OR
NSNumber *numPortrait = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
[[UIDevice currentDevice] setValue:numPortrait forKey:#"orientation"];
But no one of them worked. Of course the last example shouldn't be an option, because if apple will change anything of their api this could cause problems inside your app.
I also tried to use the AppDelegate method and always determine the orientation inside this method after looking for the correct UIInterfaceOrientation of the actual visibleViewController but then it sometimes happened to crash when switching from one to another orientation. So I'm still wondering why its made so complicated and there seems also not to be any documentation where it is explained correctly.
Even following this part didn't help me.
UIViewController+OrientationPermissions.h
#interface UIViewController (OrientationPermissions)
+ (void)setSupportedOrientations:(UIInterfaceOrientationMask)supportedOrientations;
+ (UIInterfaceOrientationMask)supportedOrientations;
#end
UIViewController+OrientationPermissions.m
#implementation UIViewController (OrientationPermissions)
static UIInterfaceOrientationMask _supportedOrientations;
+ (void)setSupportedOrientations: (UIInterfaceOrientationMask)supportedOrientations {
_supportedOrientations = supportedOrientations;
}
+ (UIInterfaceOrientationMask)supportedOrientations {
return _supportedOrientations;
}
#end
In your UIApplication delegate
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
return [UIViewController supportedOrientations];
}
Then on a desired view controller do something like
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[UIViewController setSupportedOrientations:UIInterfaceOrientationMaskAll];
}
Don't forget to reset mask before leaving this view controller
Note, if you are using UINavigationController or UITabBarController, see https://stackoverflow.com/a/28220616/821994 how to bypass that
Defiantly work Please try.
I solve after 2 days
//AppDelegate.m - this method is not available pre-iOS6 unfortunately
- (NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window{
NSUInteger orientations = UIInterfaceOrientationMaskAllButUpsideDown;
if(self.window.rootViewController){
UIViewController *presentedViewController = [[(UINavigationController *)self.window.rootViewController viewControllers] lastObject];
orientations = [presentedViewController supportedInterfaceOrientations];
}
return orientations;
}
//MyViewController.m - return whatever orientations you want to support for each UIViewController
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
I'm in a real mess right now . I used apples sample code which does this :
Create a portrait view controller and a landscape view controller
Potrait event controller then registers for device orientation changed notifications
When device is rotated it presents a modal view controller for the landscape view or dismisses the landscape view if it is rotated back to portrait .
Everythings working as it should except for a little problem ....
Now to my problem . I used this to launch a rotatable view controller from a table view . It can be rotated and works fine . But if I initially launch it in landscape mode it will still launch as portrait . If i want landscape i have to afterwards AGAIN rotate it to landscape .I tried very hard to fix for this but failed . You can download and run the sample code from Apple Developer Site Here . Can anyone fix this code so that if launched in landscape mode it presents the modal view for the landscape view ? Otherwise I'll have to rewrite everything to use a single view controller .
These are the relevant portions of apples code :
- (void)viewDidLoad
{
self.view.backgroundColor = [UIColor colorWithRed:197.0/255.0 green:204.0/255.0 blue:211.0/255.0 alpha:1.0];
LandscapeViewController *viewController = [[LandscapeViewController alloc]
initWithNibName:#"LandscapeView" bundle:nil];
self.landscapeViewController = viewController;
[viewController release];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification object:nil];
}
- (void)orientationChanged:(NSNotification *)notification
{
// We must add a delay here, otherwise we'll swap in the new view
// too quickly and we'll get an animation glitch
[self performSelector:#selector(updateLandscapeView) withObject:nil afterDelay:0];
}
- (void)updateLandscapeView
{
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !isShowingLandscapeView)
{
[self presentModalViewController:self.landscapeViewController animated:YES];
isShowingLandscapeView = YES;
}
else if (deviceOrientation == UIDeviceOrientationPortrait && isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
isShowingLandscapeView = NO;
}
}
// override to allow orientations other than the default portrait orientation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{return (interfaceOrientation == UIInterfaceOrientationPortrait); // support only portrait}
I know this is probably no longer relevant to you, but I just came across the same glitch and here's my solution.
From the way you have your code set out (including how Apple sets it out)
- (void)updateLandscapeView
is only called once a Notification is sent out telling the ViewController of an orientation change: the issue here is, that this is the method responsible for checking the orientation it self. (i.e. Starting the application this method is not called and therefore it doesn't check whether the device is in any other orientation)
The solution is quite simple: Force cal themethod at launch, i.e. in viewDidLoad . . .
[self updateLandscapeView]
This will force call the method and check the interfaces orientation, after the first time, the method will be called again when ever it receives a notification for a changed Orientation
Hope this helps someone Out There
It seems that the device assumes portrait unless you specify landscape only in settings. Your only option would be in your portrait view in the loadview method to detect the orientation and to swap views during launch.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if(interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ) {
//Load vertical interface
}
else
{
//load landscape
}
}
After a supportive answer of miamk, I am a step closer to achieving what I want.. But there's the following issue now:
I have an iPad application that can be used in all four view modes (portrait up/down and landscape left/right). But at a certain point I have a View that I only want to be seen in landscape mode. So I do the following in the UIViewController that will trigger the action to view the landscape-only view:
- (void) showProperty:(Property *) property {
if ([self interfaceOrientation] == UIInterfaceOrientationLandscapeLeft || [self interfaceOrientation] == UIInterfaceOrientationLandscapeRight) {
PropertyViewController *propertyView = [[PropertyViewController alloc] initWithNibName:#"PropertyViewController" bundle:[NSBundle mainBundle]];
propertyView.property = property;
[self.navigationController pushViewController:propertyView animated:YES];
[propertyView release];
propertyView = nil;
}
else {
RotateDeviceViewController *rotateView = [[RotateDeviceViewController alloc] initWithNibName:#"TabRotate" bundle: [NSBundle mainBundle]];
rotateView.property = property;
[self.navigationController pushViewController:rotateView animated:YES];
[rotateView release];
rotateView = nil;
}
}
This works fine and thus shows either the desired screen (PropertyViewController) when the iPad is held in landscape mode, and if not it shows the RotateDeviceViewController which shows the user a message that he/she is supposed to rotate the device to correctly view the screen.
So that works!
Then the problem arises in this RotateDeviceViewController.. There I have the following:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
[self showProperty];
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
- (void) showProperty {
PropertyViewController *propertyView = [[PropertyViewController alloc] initWithNibName:#"PropertyViewController" bundle:[NSBundle mainBundle]];
propertyView.property = property;
[self.navigationController pushViewController:propertyView animated:YES];
[propertyView release];
}
So as soon as I rotate the device (when viewing the RotateDeviceViewController) to landscape mode I show the user the PropertyViewController. This works... But when the PropertyViewController appears it shows my layout 90 degrees rotated. So basically it shows the content in portrait mode instead of using the landscape mode (which is actually the way you are holding the device)..
I hope this makes sense and someone can show me what's causing this.
A more elegant workaround (at least in terms of design) to #MacN00b's answer, is to set up a portrait view with a message that tells the user that he should rotate the device and only when he rotates it you show the view built for landscape.
Honestly, I think its ugly to have everything already rotated when the user is still in portrait orientation.
You can listen for orientation changes using:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(orientationChanged:) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
And then respond to those by loading the appropriate view...
- (void) orientationChanged:(id)object
{
UIInterfaceOrientation interfaceOrientation = [[object object] orientation];
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
self.view = self.portraitView;
}
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
self.view = self.landscapeView;
}
}
If this is going into a dedicate UIView subclass for the screen in question you can make the portraitView contain a label notifying the user to rotate the screen to view the content and then make the landscape view contain your actual content.
I currently do this in an app and both views are contained in a single nib. Just be sure you set the orientation on the view properties in IB appropriately for each view...
Try this:
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
Hope this helps.