I am using MFMailComposeViewController for in app email and it works fine in Portrait mode. But when my app which supports both orientation is in landscape mode,
using [self presentModalViewController:mailVC animated:YES];
forces my entire app into the portrait mode which I don't want.
I have tried many things from overriding MFMailComposeViewController shouldAutorotateToInterfaceOrientation: methods, resigning the first responder and so on without much luck.
How can I get MFMailComposeViewController work in Landscape on iPad ?
I had the same problem and tried overriding, that's didn't help me.
Try to send "presentModalViewController:" to rootViewController of your application window (in AppDelegate).
I think that's works for me because MFMailComposeViewController can't force rootViewController orientation, but can force orientation of viewControllers that was pushed by rootViewController's navigationController.
You need something like this:
[((AppDelegate *)[UIApplication sharedApplication]).window.rootViewController presentViewController:mailVC animated:YES completion:nil];
Try overriding the auto rotate function:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return ((interfaceOrientation == UIInterfaceOrientationLandscapeLeft) ||
(interfaceOrientation == UIInterfaceOrientationLandscapeRight));
}
Alternatively, present the MFMailComposeViewController from the UIViewController which manages rotation within your app.
Related
I am looking to have one view in my app have landscape orientation. I have managed to get the view to stay in landscape when rotated manually, but if the device is already portrait, it stays portrait, regardless of its supported orientation (set using supportedInterfaceOrientations method) . Is there a way to get the view to rotate automatically? I have tried:
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationLandscapeRight animated:NO];
but this doesn't work.
Any suggestions would be greatly appreciated!
One way to do this is by overriding preferredInterfaceOrientationForPresentation but in order for that to be called the viewController has to be presented (as in modal) and not pushed as mentioned here:
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
NSLog(#" preferred called");
return UIInterfaceOrientationPortrait;
}
In order to present your viewController in a UINavigationController use:
UINavigationController *presentedNavController = [[UINavigationController alloc] initWithRootViewController:protraitViewController];
[self presentViewController:presentedNavController animated:YES completion:nil];
To make UINavigationController respect your current viewController's orientation preferences use this simple category instead of sub-classing.
Also, this part of Apple's documentation is a good read for understanding iOS orientation handling better.
Define the following in the UIViewController for your landscape-only view:
- (UIInterfaceOrientation) supportedInterfaceOrientations
{ return UIInterfaceOrientationLandscapeLeft; } // or right or both
This should prevent your view from ever appearing in portrait. From iOS documentation:
Declaring a Preferred Presentation Orientation
When a view controller
is presented full-screen to show its content, sometimes the content
appears best when viewed in a particular orientation in mind. If the
content can only be displayed in that orientation, then you simply
return that as the only orientation from your
supportedInterfaceOrientations method.
My application is primarily portrait, however there is one view that REQUIRES a landscape orientation.
My views are contained within a UINavigationController, which (apparently) is the cause of this issue.
All UIViewControllers except one have this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
The UIViewController that requires Landscape has this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
Now, what happens is when the user reaches the landscape UIViewController, it is shown in portrait. The user can then rotate their phone and it displays in landscape as I want it to (locking to landscape). The user then progresses onwards to a portrait UIViewController and the same happens: it start in landscape, then they rotate their phone and it becomes portrait again (and locks to portrait).
It seems orientation locking is allowed between UIViewControllers, however auto-rotation / programmatically changing the orientation is somehow blocked.
How do I force the phone to update to the correct orientation?
There is a temporary solution: I can detect the orientation of the device and show a message asking them to rotate the device if it is not correct, however this is not optimal.
I had the same requirement for one of my applications!!!
luckily I found a solution!
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];
P.S.Tested on sdk 3.2.5 ios 5.0.1.
P.S. On iOS 8 previous answer results some screen flickering and also - it is not stable (In some cases It does not work for me anymore.) So, for my needs, I changed the code to: (ARC)
//set statusbar to the desired rotation position
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft animated:NO];
[self.navigationController presentViewController:[UIViewController new] animated:NO completion:^{
dispatch_after(0, dispatch_get_main_queue(), ^{
[self.navigationController dismissViewControllerAnimated:NO completion:nil];
});
}];
//I added this code in viewDidDissapear on viewController class, which will be popped back.
Hopefully it will help!
This might help. You can call the following method upon appearing, where appropriate. e.g. in -viewWillAppear:animated
attemptRotationToDeviceOrientation
Attempts to rotate all windows to the orientation of the device.
+ (void)attemptRotationToDeviceOrientation
Discussion
Some view controllers may want to use app-specific conditions to
determine the return value of their implementation of the
shouldAutorotateToInterfaceOrientation: method. If your view
controller does this, when those conditions change, your app should
call this class method. The system immediately attempts to rotate to
the new orientation. A rotation occurs so long as each relevant view
controller returns YES in its implementation of the
shouldAutorotateToInterfaceOrientation: method.
Availability
Available in iOS 5.0 and later.
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIViewController_Class/Reference/Reference.html
Use this,
[[UIDevice currentDevice]performSelector:#selector(setOrientation:) withObject:(__bridge id)((void *)UIInterfaceOrientationLandscapeRight)];
I have two views, on first view i have multiple buttons, i wanted that view to be landscape right so i put this code :
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
it works fine as my application starts in landscape right mode. the buttons on this view pushes user to second view which i wanted to be a portrait mode. so i put that code in second view but its not working as i wanted.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
its showing my the orientation which i had on previous view. should i have to put something on viewdidload to make it works? please help. thanks
It's a bit a hack, but here it is. In the -(void)viewDidLoad of the viewController that you want to force portrait for do the following:
UIViewController *viewController = [[UIViewController alloc] init];
[self presentModalViewController:viewController animated:NO];
[self dismissModalViewControllerAnimated:NO];
[viewController release];
This basically forces portrait, by presenting a controller (which only supports portrait by default).
You should use one orientation for all view controllers. Otherwise you may have some problems. For example in your case imagine that the user has switched to the second view and has changed the orientation to UIInterfaceOrientationPortrait. Now he returns to the first screen and all buttons are on the wrong positions! Actually the problem is that the orientation will not change to UIInterfaceOrientationLandscapeRight back as you expect. It happens because shouldAutorotateToInterfaceOrientation is not called after viewWillAppear is triggered. So you have to code some dirty hacks to force orientation change. It is not a good solution.
By the way, how do you change your controllers? If you are using addSubview, then your second controller's shouldAutorotateToInterfaceOrientation will not be triggered at all. When using addSubview you should handle it by yourself.
I've created a modalviewcontroller and all the subviews are created by code. When I'm testing the app, I find a problem. Then main cause of the problem is that an app shouldn't support UpsideDown orientation, but devices may happen to be in that orientation.
If I:
Rotate the device to Portrait orientation, and then to UpsideDown mode and presentModalView, the subviews in modalviewcontroller should appear the same as Portrait orientation.
Rotate the device to Landscape orientation, and then to UpsideDown mode and presentModalView, the subviews should be treated differently.
The above situation tells me that I should create subviews in modalviewcontroller according to previous InterfaceOrientation.
The problem is: How to get the previous screen's InterfaceOrientation? Getting the device orientation won't do any help in this situation.
PS: I'm writing a lib, I may give my users the interface to send me the "toInterfaceOrientation" from -willRotateToInterfaceOrientation:duration: but are there any ideas about how to get the orientation in my code?
In any UIViewController you can access the property interfaceOrientation like this:
if (self.interfaceOrientation == UIInterfaceOrientationPortrait) {
// do stuff
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
In my iPad app, I need to run some layout code to set the proper layout depending on the orientation. By default, the layout is configured for the landscape orientation, so in the case that the app starts in portrait mode, I need to take extra action to configure the views properly for display in portrait.
In my -application:didFinishLaunchingWithOptions: method, I check the orientation using [[UIDevice currentDevice] orientation]. The problem here is that it always returns portrait even if the app is starting in landscape. Is there any way around this?
This is expected behavior. Quoth the UIViewController documentation:
Note: At launch time, applications should always set up their interface in a portrait orientation. After the application:didFinishLaunchingWithOptions: method returns, the application uses the view controller rotation mechanism described above to rotate the views to the appropriate orientation prior to showing the window.
In other words, as far as the device is concerned the orientation is portrait while the application is launching. At some point after application:didFinishLaunchingWithOptions: it will detect the different orientation and call your shouldAutorotateToInterfaceOrientation: method and then your other view rotation methods, which you should handle as normal.
This is the best way to check for orientation on launch. First, create a new method in your AppDelegate that checks the orientation:
-(void)checkLaunchOrientation:(id)sender{
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
BOOL isLandscape = UIDeviceOrientationIsLandscape(self.viewController.interfaceOrientation);
if (UIInterfaceOrientationIsLandscape(orientation) || isLandscape) {
//do stuff here
}
}
At the end of -application:didFinishLaunchingWithOptions: run
[self performSelectorOnMainThread:#selector(checkLaunchOrientation:) withObject:nil waitUntilDone:NO];
Use self.interfaceOrientation in your view controller - it's a property of UIViewController that is set by iOS for you, and in some cases is more reliable than [[UIDevice currentDevice] orientation].
Here's a detailed description: http://bynomial.com/blog/?p=25
As mentioned in a blog post above, there is a set of macros for testing orientation. That blog post however mentions UIDeviceOrientationIsPortrait. I like the following below, it's a minor twist.
if(UIInterfaceOrientationIsPortrait(self.interfaceOrientation))
{
NSLog(#"Portrait");
}
else
{
NSLog(#"Landscape");
}
An observation I've made is that you can't call this code in a table view, pushed on to a Navigation Controller embedded in the split view controller. So in other words you can't call it from the master view controller. You have to replace the "self.interfaceOrientation" with splitviewcontroller.interfaceOrientation, assuming you maintain a reference to the parent split view controller.
Use the status bar orientation instead to detect it.
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
then perform the if's on the "orientation" you have obtained from above.
So the question is about checking orientation at startup. The answer is sadly "You can't".
But AFTER startup, you can check orientation the normal way (as others have described).
If anyone else comes here looking for an answer, simply stop looking since, at startup the orientation variable is not set (all views frames/bounds also report being in portrait even if they aren't).
You want to make sure you set the proper keys in your Info.plist to allow for the orientations you want:
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
Not that you need another answer, but I thought I should add that you almost never want to use [[UIDevice currentDevice] orientation]. That method returns the orientation of the device, which isn't necessarily the same as the orientation of the interface.
It's not true that you can't figure out the launch orientation, it is true that it's a pain in the rear to do so.
here's what you need to do.
your first UIViewController needs to have some special logic to nab the information you'd like.
you might even want to create a UIStartupController just for these purposes if it's that important to your flow.
in the case of my project, we already had such a startup controller present.
all you need is the following code
-(id) initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
self.launchOrientation = UIDeviceOrientationUnknown;
}
return self;
}
-(void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
if (self.launchOrientation == UIDeviceOrientationUnknown && duration > 0)
self.launchOrientation = UIInterfaceOrientationPortrait;
else
self.launchOrientation = toInterfaceOrientation;
}
basically, if we're not launching in UIInterfaceOrientationPortrait, the first rotation callback sequence will actually reveal the launch orientation.
if launched in UIInterfaceOrientationPortrait, then we need to check that the first rotation's duration is non zero, and then we know that it was launched from portrait.