Objective c - UIActivityViewController orientation mode - iphone

My iPhone app is design to support portrait orientation only, except one view controller that present photos, and support landscape mode as well.
So overall my project support all the orientations, but every view controller (except the photo view controller) implement the method:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
The problem:
In one of my (only portrait) view controller, I'm using UIActivityViewController to let the user choose sharing content through email or SMS.
When the user pick the email option, it automatically shows the ios native email view controller.
If the user now change the device's orientation to landscape, the email view controller also changes to landscape, now if the user tap the cancel/send button, the email controller get dismissed, and now my all app is in landscape!
Is there a way to force the eamil view controller to be in portrait mode only?
I know I can create my own email view controller and subclass UIActivity to show it, but than the UIActivityViewController won't show the default email icon when presented, and I really like it to show it and not some other (must be grey) icon that I'll provide.

You should make another uinavigationcontroller and present uiactivityviewcontroller from there.
In this code _image is a UIImage yo wan to share.
BlankViewController is just place holder viewcontroller you can create in IB you can also make it's view's background colour to clear and do what ever appearance changes you want to do.
__weak CurrentViewController *weakSelf = self ;
UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:#[_image] applicationActivities:nil] ;
UIViewController *viewC = [[UIViewController alloc] initWithNibName:#"BlankViewController" bundle:nil] ;
UINavigationController *navC = [[UINavigationController alloc] initWithRootViewController:viewC] ;
activityViewController.completionHandler = ^(NSString *activityType, BOOL completed)
{
[weakSelf dismissViewControllerAnimated:NO completion: ^ {
; // Your completion handler
}] ;
};
[self presentViewController:navC animated:NO completion: ^ {
[navC presentViewController:activityViewController animated:YES completion: ^ {
;
}] ;
}];

i had similar problems in the app i am currently developing. i ended up overwriting more of the rotation methods to make sure my own viewcontroller stays in portrait.
that's what worked for me (IOS5+):
- (BOOL) shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationPortrait;
}
- (BOOL)shouldAutorotateToInterfaceOrientation (UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
if you are pre ios5 or that's not working for you have a look at:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIViewController_Class/Reference/Reference.html
hope you get it to work. :)

May be u should try
- (void)viewWillAppear:(BOOL)animated
{
[self shouldAutorotateToInterfaceOrientation: UIInterfaceOrientationLandscapeRight];
[super viewWillAppear:animated];
}
on your view?

Couldn't found a solution to this problem, so I ended up implementing my own email UIActivity subclass...

Related

Turn screen in landscape mode when iPhone is in landscape

I want to make an iPhone app which shows a view when iPhone is in portrait mode, and ANOTHER when iPhone is in landscape mode.
I know there is many post about that but I don't understant the answer.
In a first time, to understand, I make test with a Tabbed Application, because I have already two views. When I tap on the second screen, I would like my iphone in landscape mode.
(and in the first one in portrait mode).
On Apple website, and stackoverflow, I saw the following code :
- (NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeLeft;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)orientation
{
if ((orientation == UIInterfaceOrientationPortrait) ||
(orientation == UIInterfaceOrientationLandscapeLeft))
return YES;
return NO;
}
Or a similar code.
In my mainstoryboard, I put the second view in landscape, with the interface.
But when I run my app, and I tap on second screen, iPhone stay in portrait mode..
I tried to do the same thing with a single view app, and created new file (landscapeViewController) with .xib file, but I can't have a godd result!
First, in storyboard, create segues from your portrait view controller to your landscape view controller and vice-versa. Then, in your portrait view controller, do this:
- (void)viewWillLayoutSubviews
{
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
[self performSegueWithIdentifier:#"SegueToLandscapeViewController" sender:self];
}
}
In your landscape view controller, do this:
- (void)viewWillLayoutSubviews
{
if (!UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
[self performSegueWithIdentifier:#"SegueToPortraitViewController" sender:self];
}
}
I succeeded with the following code :
-(void)orientationDidChanged: (NSNotification *)notification {
UIDeviceOrientation devOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(devOrientation)) {
UIStoryboard *main = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
landscapeViewController *landscape = [main instantiateViewControllerWithIdentifier:#"landscape"];
[self presentViewController:landscape animated:YES completion:nil];
}
else if (UIDeviceOrientationIsPortrait(devOrientation)) {
[self dismissViewControllerAnimated:YES completion:nil];
}
}
But I can't change transition between View Controller, whereas I can between simple View.
Maybe it's because I have two views controllers linked to the same view controller.h...?

IOS6 rotation issue

I know you have to use the new rotation methods for IOS6, but it seems the method I've written doesn't work.
I setted my plist file to allow all rotation but not portraitUpsideDown
I then had the following in my appDelegate:
self.navController = [[UINavigationController alloc] initWithRootViewController:self.viewController];
[self.window setRootViewController:navController]; //add nav controller to be the root view
Then in my rootView, to push to another controller, I have:
WebViewViewController *webController = [[JBWebViewViewController alloc] init];
webController.urlString = urlName;
[self.navigationController pushViewController:webController animated:YES];
And In the web controller I have:
#pragma mark - System Rotation Methods
//for any version before 6.0
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
//only allow landscape
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
/for 6.0+
- (BOOL)shouldAutorotate{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
What I want do, is to allow 3 rotations in the root view, but when switch to the web view(note I do push navigation, not add subview), I only want to allow portrait view.
Someone help me please
-------UPDATE----------
I've created my own navController subclass of UINavigationController, I have an BOOL landscapeModeOn that I can setup to tell auto rotation specs
#pragma mark - System Rotation Methods
//for any version before 6.0
- (BOOL)shouldAutorotateToInterfaceOrientation (UIInterfaceOrientation)interfaceOrientation
{
if (landscapeModeOn) {
return interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown;
} else {
return interfaceOrientation == UIInterfaceOrientationPortrait;
}
}
//for 6.0+
- (NSUInteger)supportedInterfaceOrientations{
if (landscapeModeOn) {
return UIInterfaceOrientationMaskAllButUpsideDown;
} else {
return UIInterfaceOrientationMaskPortrait;
}
}
- (BOOL)shouldAutorotate{
UIInterfaceOrientation ori = [UIDevice currentDevice].orientation;
if (landscapeModeOn) {
return ori != UIInterfaceOrientationPortraitUpsideDown;
} else {
return ori == UIInterfaceOrientationPortrait;
}
}
IN the subviews loading, I do:
- (void)viewWillAppear:(BOOL)animated{
//get nav controller and turn off landscape mode
JBNavController *navController = (JBNavController*)self.navigationController;
[navController setLandscapeModeOn:NO];
[navController shouldAutorotate];
}
--------------------Refer to best answer's quote
For IOS6, apple is now focusing on using the Storyboard's AutoLayout together with the new rotation definitions, it is difficult to fix some tiny bugs for IOS6 based on the ios 4.3 and ios 5 coding structure
From applefreak, his suggestion hinted on:
A main challenge in your case is not handling the orientations. Actually it's locking the different view controllers to particular orientation
Although manual rotate view seems really hard to do without any bugs, but it seems the only solution I am now trying, will post more once solved
For your situation you will have to subclass your NavigationController and add the shouldAutorotate and supportedInterfaceOrientations methods to it. iOS6 now asks your navigation stack in the reverse order to iOS5 so it will ask your NavigationController first and if that returns YES it won't even consult with it's child view controllers. To fix that you have to add the logic yourself to do this
So in your subclassed navigation controller you manually ask your current viewcontroller it's autorotation abilities:
- (BOOL)shouldAutorotate
{
return self.topViewController.shouldAutorotate;
}
- (NSUInteger)supportedInterfaceOrientations
{
return self.topViewController.supportedInterfaceOrientations;
}
and in your individual viewcontrollers you can now implement those functions and have them return the values you want which you have defined in your question.
I hope this makes sense.
Following code is wrong!
- (BOOL)shouldAutorotate{
return NO;
}
- (NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
Remember that supportedInterfaceOrientations gets called only if shouldAutoRotate returns YES. Now root view controllers decides whether it's children rotates or not.
In your case I would suggest to have a base class controller to your self.viewController and set self.viewController to root view controller not navigationController otherwise rotation methods won't be invoked! I ran into this same issue. You should have a HAS-A relationship with base view controller and it's children. Return Yes/No from ShouldAutoRotate based on active children and same for supported orientation. If you follow this architecture then it would be consistent for complex App.
For example in your case BaseviewController should return YES from shouldAutoRotate and returns UIInterfaceOrientationPortrait from supported orientation delegate when webviewController is active. I hope this makes sense.
It common code for iOS5 and iOS6
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsLandscape(interfaceOrientation))
{
// here to implement landscope code
}
else
{
// here to implement setframePortrait
}
}

IOS 6 force device orientation to landscape

I gave an app with say 10 view controllers. I use navigation controller to load/unload them.
All but one are in portrait mode. Suppose the 7th VC is in landscape. I need it to be presented in landscape when it gets loaded.
Please suggest a way to force the orientation go from portrait to landscape in IOS 6 (and it will be good to work in IOS 5 as well).
Here is how I was doing it BEFORE IOS 6:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIViewController *c = [[[UIViewController alloc]init] autorelease];
[self presentModalViewController:c animated:NO];
[self dismissModalViewControllerAnimated:NO];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
Presenting and dismissing a modal VC was forcing the app to review its orientation, so shouldAutorotateToInterfaceOrientation was getting called.
What I have have tried in IOS 6:
- (BOOL)shouldAutorotate{
return YES;
}
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationLandscapeLeft;
}
On load, the controller keeps staying in portrait. After rotating the device, the orientation changes just ok. But I need to make the controller to rotate automatically to landscape on load, thus the user will have to rotate the device to see the data correctly.
Another problem: after rotating the device back to portrait, the orientation goes to portrait, although I have specified in supportedInterfaceOrientations only UIInterfaceOrientationMaskLandscape. Why it happens?
Also, NONE of above 3 methods are getting called.
Some (useful) data:
In my plist file I have specified 3 orientations - all but upside down.
The project was started in Xcode 4.3 IOS 5. All classes including xibs were created before Xcode 4.5 IOS 6, now I use the last version.
In plist file the status bar is set to visible.
In xib file (the one I want to be in landscape) the status bar is "None", the orientation is set to landscape.
Any help is appreciated. Thanks.
Ok, folks, I will post my solution.
What I have:
A view based application, with several view controllers. (It was navigation based, but I had to make it view based, due to orientation issues).
All view controllers are portrait, except one - landscapeLeft.
Tasks:
One of my view controllers must automatically rotate to landscape, no matter how the user holds the device. All other controllers must be portrait, and after leaving the landscape controller, the app must force rotate to portrait, no matter, again, how the user holds the device.
This must work as on IOS 6.x as on IOS 5.x
Go!
(Update Removed the macros suggested by #Ivan Vučica)
In all your PORTRAIT view controllers override autorotation methods like this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
}
-(BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskPortrait;
}
You can see the 2 approaches: one for IOS 5 and another For IOS 6.
The same for your LANDSCAPE view controller, with some additions and changes:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation{
[image_signature setImage:[self resizeImage:image_signature.image]];
return (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
-(BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
[image_signature setImage:[self resizeImage:image_signature.image]];
return UIInterfaceOrientationMaskLandscapeLeft;
}
ATTENTION: to force autorotation in IOS 5 you should add this:
- (void)viewDidLoad{
[super viewDidLoad];
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 6.0)
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationLandscapeLeft animated:NO];
}
Analogically, after you leave the LANDSCAPE controller, whatever controller you load, you should force again autorotation for IOS 5, but now you will use UIDeviceOrientationPortrait, as you go to a PORTRAIT controller:
- (void)viewDidLoad{
[super viewDidLoad];
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 6.0)
[[UIApplication sharedApplication] setStatusBarOrientation:UIDeviceOrientationPortrait animated:NO];
}
Now the last thing (and it's a bit weird) - you have to change the way you switch from a controller to another, depending on the IOS:
Make an NSObject class "Schalter" ("Switch" from German).
In Schalter.h say:
#import <Foundation/Foundation.h>
#interface Schalter : NSObject
+ (void)loadController:(UIViewController*)VControllerToLoad andRelease:(UIViewController*)VControllerToRelease;
#end
In Schalter.m say:
#import "Schalter.h"
#import "AppDelegate.h"
#implementation Schalter
+ (void)loadController:(UIViewController*)VControllerToLoad andRelease:(UIViewController*)VControllerToRelease{
//adjust the frame of the new controller
CGRect statusBarFrame = [[UIApplication sharedApplication] statusBarFrame];
CGRect windowFrame = [[UIScreen mainScreen] bounds];
CGRect firstViewFrame = CGRectMake(statusBarFrame.origin.x, statusBarFrame.size.height, windowFrame.size.width, windowFrame.size.height - statusBarFrame.size.height);
VControllerToLoad.view.frame = firstViewFrame;
//check version and go
if (IOS_OLDER_THAN_6)
[((AppDelegate*)[UIApplication sharedApplication].delegate).window addSubview:VControllerToLoad.view];
else
[((AppDelegate*)[UIApplication sharedApplication].delegate).window setRootViewController:VControllerToLoad];
//kill the previous view controller
[VControllerToRelease.view removeFromSuperview];
}
#end
NOW, this is the way you use Schalter ( suppose you go from Warehouse controller to Products controller ) :
#import "Warehouse.h"
#import "Products.h"
#implementation Warehouse
Products *instance_to_products;
- (void)goToProducts{
instance_to_products = [[Products alloc] init];
[Schalter loadController:instance_to_products andRelease:self];
}
bla-bla-bla your methods
#end
Of course you must release instance_to_products object:
- (void)dealloc{
[instance_to_products release];
[super dealloc];
}
Well, this is it. Don't hesitate to downvote, I don't care. This is for the ones who are looking for solutions, not for reputation.
Cheers!
Sava Mazare.
This should work, it's similar to the pre-iOS 6 version, but with a UINavigationController:
UIViewController *portraitViewController = [[UIViewController alloc] init];
UINavigationController* nc = [[UINavigationController alloc] initWithRootViewController:portraitViewController];
[self.navigationController presentModalViewController:nc animated:NO];
[self.navigationController dismissModalViewControllerAnimated:NO];
I'm calling this before I'm pushing the next UIViewController. It will force the next pushed UIViewController to be displayed in Portrait mode even if the current UIViewController is in Landscape (should work for Portrait to Landscape too). Works on iOS 4+5+6 for me.
I think that best solution is to stick to official apple documentation. So according to that I use following methods and everything is working very well on iOS 5 and 6.
In my VC I override following methods:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return UIInterfaceOrientationIsPortrait(interfaceOrientation);
}
Methods for iOS 6, first method returns supported orientation mask (as their name indicate)
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
second one thats tells your VC which is preferred interface orientation when VC is going to be displayed.
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationPortrait;
}
Just change Portrait for orientation that you want ;)
This solution is working smooth, I don't like the idea of creating macros and other stuff, that goes around this simple solution.
Hope this help...
I had the same problem, 27 views in my application from which 26 in portrait and only one in all orientations ( an image viewer :) ).
Adding the macro on every class and replace the navigation wasn't a solution I was comfortable with...
So, i wanted to keep the UINavigationController mechanics in my app and not replace this with other code.
What to do:
#1 In the application delegate in method didFinishLaunchingWithOptions
if ([[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
// how the view was configured before IOS6
[self.window addSubview: navigationController.view];
[self.window makeKeyAndVisible];
}
else
{
// this is the code that will start the interface to rotate once again
[self.window setRootViewController: self.navigationController];
}
#2
Because the navigationController will just responde with YES for autorotation we need to add some limitations:
Extend the UINavicationController -> YourNavigationController and link it in the Interface Builder.
#3 Override the "anoying new methods" from navigation controller.
Since this class is custom only for this application it can take responsibility
for it's controllers and respond in their place.
-(BOOL)shouldAutorotate {
if ([self.viewControllers firstObject] == YourObject)
{
return YES;
}
return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
if ([self.viewControllers firstObject] == YourObject)
{
return UIINterfaceOrientationMaskLandscape;
}
return UIInterfaceOrientationMaskPortrait;
}
I hope this will help you,
From the iOS 6 Release Notes:
Now, iOS containers (such as UINavigationController) do not consult their children to determine whether they should autorotate.
Does your rootViewController pass the shouldAutoRotate message down the ViewController hierarchy to your VC?
I used the same method as OP pre-ios6 (present and dismiss a modal VC) to show a single view controller in landscape mode (all others in portrait). It broke in ios6 with the landscape VC showing in portrait.
To fix it, I just added the preferredInterfaceOrientationForPresentation method in the landscape VC. Seems to work fine for os 5 and os 6 now.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
- (BOOL)shouldAutorotate
{
return NO;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation
{
return UIInterfaceOrientationLandscapeLeft;
}
Hey guys after tryng a lot of different possible solutions with no success i came out with the following solution hope it helps!.
I prepared a recipe :).
Problem:
you need change orientation of viewcontrollers using navigationcontroller in ios 6.
Solution:
step 1. one initial UIviewcontroler to trigger modal segues to landscape and
portrait UInavigationControllers as picture shows....
more deeply in UIViewController1 we need 2 segues actions according to global variable at Appdelegate....
-(void)viewDidAppear:(BOOL)animated{
if([globalDelegate changeOrientation]==0){
[self performSegueWithIdentifier:#"p" sender:self];
}
else{
[self performSegueWithIdentifier:#"l" sender:self];
}
}
also we need a way back to portrait &| landscape....
- (IBAction)dimis:(id)sender {
[globalDelegate setChangeOrientation:0];
[self dismissViewControllerAnimated:NO completion:nil];
}
step 2. the first Pushed UiViewControllers at each NavigationController goes
with...
-(NSUInteger)supportedInterfaceOrientations{
return [self.navigationController supportedInterfaceOrientations];
}
-(BOOL)shouldAutorotate{
return YES;
}
step 3. We overwrite supportedInterfaceOrientations method at subclass of UInavigationController....
in your customNavigationController we have .....
-(NSUInteger)supportedInterfaceOrientations{
if([self.visibleViewController isKindOfClass:[ViewController2 class]]){
return UIInterfaceOrientationMaskPortrait;
}
else{
return UIInterfaceOrientationMaskLandscape;
}
}
step 4. At storyboard or by code, set wantsFullScreenLayout flag to yes, to both portrait and landscape uinavigationcontrollers.
Try segueing to a UINavigationController which uses a category or is subclassed to specify the desired orientation, then segue to the desired VC. Read more here.
As an alternative you can do the same using blocks:
UIViewController *viewController = [[UIViewController alloc] init];
viewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentViewController:viewController animated:NO completion:^{
[self dismissViewControllerAnimated:NO completion:nil];
}];
Also, call it before pushing the new view.
Go to you Info.plist file and make the change
I had the same problem. If you want to force a particular view controller to appear in landscape, do it right before you push it into the navigation stack.
UIInterfaceOrientation currentOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (currentOrientation == UIInterfaceOrientationPortrait ||
currentOrientation == UIInterfaceOrientationPortraitUpsideDown)
[[UIDevice currentDevice] setOrientation:UIInterfaceOrientationLandscapeLeft];
UIViewController *vc = [[UIViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
I solved it by subclassing UINavigationController and overriding the supportedInterfaceOrientations of the navigation Controller as follow:
- (NSUInteger)supportedInterfaceOrientations
{
return [[self topViewController] supportedInterfaceOrientations];
}
All the controllers implemented supportedInterfaceOrientations with their desired orientations.
I have used the following solution. In the one view controller that has a different orientation than all the others, I added an orientation check in the prepareForSegue method. If the destination view controller needs a different interface orientation than the current one displayed, then a message is sent that forces the interface to rotate during the seque.
#import <objc/message.h>
...
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if(UIDeviceOrientationIsLandscape(self.interfaceOrientation))
{
UIInterfaceOrientation destinationOrientation;
if ([[segue destinationViewController] isKindOfClass:[UINavigationController class]])
{
UINavigationController *navController = (UINavigationController *)[segue destinationViewController];
destinationOrientation = [navController.topViewController preferredInterfaceOrientationForPresentation];
} else
{
destinationOrientation = [[segue destinationViewController] preferredInterfaceOrientationForPresentation];
}
if ( destinationOrientation == UIInterfaceOrientationPortrait )
{
if ([[UIDevice currentDevice] respondsToSelector:#selector(setOrientation:)])
{
objc_msgSend([UIDevice currentDevice], #selector(setOrientation:), UIInterfaceOrientationPortrait );
}
}
}
}

Storyboards orientation support for xCode 4.2?

I upgraded to xCode 4.2 and it's new Storyboards feature. However, could not find a way to support both portrait and landscape.
Of course, I did it programmatically, with 2 views, one for portrait and one for landscape, like in old days, and:
if (interfaceOrientation == UIInterfaceOrientationLandscapeLeft || interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
self.view = self.landscapeView;
}
else
{
self.view = self.portraitView;
}
But I was looking for a way to do this automatically somehow. I mean, it's xCode 4.2 now, I expected more from it. Thanks all.
==================================
TEMPORARY SOLUTION:
I will present here a temporary solution. I say it's temporary, because I am still waiting for Apple guys to do something really intelligent about this.
I created another .storyboard file, called "MainStoryboard_iPhone_Landscape", and implemented the landscape view controllers there. Actually, it's exactly like normal(portrait) .storyboard, but all screens are in landscape mode.
So I will extract the ViewController from landscape storyboard, and when rotation occurs, just change self.view with the new viewController's view.
1.Generate Notifications when orientation changes:
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
2.Look for notifications:
[[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceOrientationDidChangeNotification object:nil queue:nil usingBlock:^(NSNotification *note) {
// 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];
}];
3.Implement updateLandscapeView
- (void)updateLandscapeView {
//> isShowingLandscapeView is declared in AppDelegate, so you won't need to declare it in each ViewController
UIDeviceOrientation deviceOrientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(deviceOrientation) && !appDelegate().isShowingLandscapeView)
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone_Landscape" bundle:[NSBundle mainBundle]];
MDBLogin *loginVC_landscape = [storyboard instantiateViewControllerWithIdentifier:#"MDBLogin"];
appDelegate().isShowingLandscapeView = YES;
[UIView transitionWithView:loginVC_landscape.view duration:0 options:UIViewAnimationOptionTransitionCrossDissolve|UIViewAnimationCurveEaseIn animations:^{
//> Setup self.view to be the landscape view
self.view = loginVC_landscape.view;
} completion:NULL];
}
else if (UIDeviceOrientationIsPortrait(deviceOrientation) && appDelegate().isShowingLandscapeView)
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:[NSBundle mainBundle]];
MDBLogin *loginVC = [storyboard instantiateViewControllerWithIdentifier:#"MDBLogin"];
appDelegate().isShowingLandscapeView = NO;
[UIView transitionWithView:loginVC.view duration:0 options:UIViewAnimationOptionTransitionCrossDissolve|UIViewAnimationCurveEaseIn animations:^{
//> Setup self.view to be now the previous portrait view
self.view = loginVC.view;
} completion:NULL];
}}
Good luck to everybody.
P.S: I will accept Ad Taylor's answer, because, after much time waiting and searching for a solution, I finished up implementing something inspired from his answer. Thanks Taylor.
This is an old question but I read this earlier in the day and then had to spend a fair amount of time work out a better solution. I came up with this solution from hacking up the Apple Alternate View example. Basically it is serving up a modal view for the landscape view.
#pragma mark Rotation view control
- (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) && !self.isShowingLandscapeView)
{
[self performSegueWithIdentifier: #"toLandscape" sender: self];
self.isShowingLandscapeView = YES;
}
else if (deviceOrientation == UIDeviceOrientationPortrait && self.isShowingLandscapeView)
{
[self dismissModalViewControllerAnimated:YES];
self.isShowingLandscapeView = NO;
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
You shouldn't generally be thinking of separate views for different orientations unless they are widely different (which, arguably, they shouldn't be). Instead, you should rely on autoresizing masks to lay out as much of your view's content based on basic restraints when the superview's frame changes. This will allow subviews to respond appropriately to a change in their superview's frame, often as a result of an interface orientation change.
To answer your question more directly, no, there is no way for Xcode to assume or be told which views you want to use for a particular interface orientation as this was never the intent of UIKit's view architecture.
Here is more information about autoresizing masks: Handling Layout Changes Automatically Using Autoresizing Rules.
In XCode v4.2.1 when using StoryBoards you can only change the orientation of the View Controller, and not the View itself, so if you have inserted another view there you wouldn't be able to change it's orientation, even if you could see the View properly.
So the previous way of having two Views would not appear to work when using StoryBoards (when using NIB's where the View Orientation is changeable for separate Views).

Autorotation lock, programmatically

Does anybody know if there is a possibility to lock autorotation of iPhone programmatically for just one view?
I want to make some kind of help with semi-transculent view, but I want to support only landscape orientation even all other views can rotate.
So I wish to lock rotation when this view is on the top.
tnx
EDIT: More details: one UIController has 7 UIView...and I wish to lock the autorotation just when the last one occurs on the top.
This question was asked over a year ago but the accepted method is now deprecated in iOS 6 so if anybody is interested in doing this in iOS 6 then you need to use supportedInterfaceOrientations on the topmost controller.
Imagine you have a setup like this...
Tab Bar Controller
Navigation Controller
View Controller
... then you need to set the supportedInterfaceOrientations method on the tab bar controller.
Create a subclass of the tab bar controller (or navigation controller if that is at the top) and set these methods in it...
- (BOOL)shouldAutorotate {
//Use this if your root controller is a navigation controller
return self.visibleViewController.shouldAutorotate;
//Use this if your root controller is a tab bar controller
return self.selectedViewController.shouldAutorotate;
}
- (NSUInteger)supportedInterfaceOrientations {
//Navigation Controller
return self.visibleViewController.supportedInterfaceOrientations;
//Tab Bar Controller
return self.selectedViewController.supportedInterfaceOrientations;
}
... then in your individual view controllers you can set the properties you want...
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations{
//return supported orientation masks
return UIInterfaceOrientationMaskLandscape;
}
Use the following...
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
You can attach it to the window. After the view is loaded, do
[self.view.window addSubview:yourStaticView];
[self.view.window bringSubviewToFront:yourStaticView]; // Do only if necessary
Remove it when leaving this view. Probably in viewWillDisappear: or viewDidDisappear:.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
//If you don't want to support multiple orientations uncomment the line below
return (toInterfaceOrientation == UIInterfaceOrientationPortrait);
//return [super shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
}
i feel there are many specific answers that work intermittently, but none provide insight as to what the ramifications or side effects are, in respect to the rest of the app or other view controllers, if the user starts tilting the phone outside of the view controller you want to control the orientation for...
after playing around with it, you may realize (like myself) that adverse or undesired results may occur (ie. orientation changes occur when you don't want them to, or vice versa).
my main realization involved that only the 'root view controller' will invoke 'shouldAutorotate', and NOT just any individual view controller you attempt to override with.
with this realization it seemed quite difficult to 'lock' a specific orientation for a specific view controller.
(meaning have vc_A always be portrait and not allowed to change to landscape, while having vc_B always be landscape and not allowed to change to portrait)
after acknowledging this, the following algorithm is what worked for me in being able to only rotate on specified view controllers.
setup:
first you have to allow the orientations you desire, in either the info.plist or the main project settings file (these orientations will be the only ones you can use in your code)
code:
1) in my root view controller (here: MasterViewController) i designated a BOOL property (allowAutorotate) that will be utilized when 'shouldAutorotate' is invoked.
2) also make the root view controller a singleton so its easily accessible from any other child view controller (without having to pass around references).
note: you may also use the observer/notification pattern or delegation or some other pattern, but for me the singleton pattern was easiest
3) add the delegate '-(BOOL)shouldAutorotate' and utilize the BOOL allowAutorotate for its return
4) create an instance method 'setInterfaceOrientation'. some other class will call this method in their 'viewDidLoad' and/or in their 'viewWillDisappear'
// 1)
#implementation MasterViewController {
BOOL allowAutorotate;
}
// 2)
+ (id)sharedMasterViewController {
static MasterViewController *sharedMasterViewController = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMasterViewController = [[self alloc] init];
});
return sharedMasterViewController;
}
- (id)init
{
self = [super init];
if (self)
{
allowAutorotate = NO;
}
return self;
}
// 3)
- (BOOL)shouldAutorotate
{
return allowAutorotate;
}
// 4)
- (void)setInterfaceOrientation:(NSInteger)orientation
{
allowAutorotate = YES;
NSNumber *value = [NSNumber numberWithInt:orientation];
[[UIDevice currentDevice] setValue:value forKey:#"orientation"];
allowAutorotate = NO;
}
5) finally in some other class get the root view controller and invoke 'setInterfaceOrientation' accordingly
// 5)
#import "MasterViewController.h"
#implementation SomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
[[MasterViewController sharedMasterViewController] setInterfaceOrientation:UIInterfaceOrientationLandscapeRight];
}
- (void)viewWillDisappear:(BOOL)animated
{
[[MasterViewController sharedMasterViewController] setInterfaceOrientation:UIDeviceOrientationPortrait];
}
notes:
1) the result of this example should be that the app will initially load in portrait, then when you load 'SomeViewController', it will change to landscape, and then when you remove it, it will change back to portrait.
2) it works like this...
every time you physically tilt the phone, the delegate 'shouldAutorotate' is invoked (only from the 'root view controller'),
as well every time you programmatically tilt the phone
NSNumber *value = [NSNumber numberWithInt:orientation];
[[UIDevice currentDevice] setValue:value forKey:#"orientation"];
the delegate 'shouldAutorotate' is invoked.
this is why we first 'allowAutorotate = YES;', then 'tilt the phone', then 'allowAutorotate = NO;'
hence, we have a result of only allowing/performing the orientation change once, programmatically, exactly when we want to.
glhf!
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { 
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
I have found very simple and working solution. By adding the top view I set a BOOL which then controls rotating.
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations; we support all.
if (helpViewOnTheTop) {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft) || (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
else
{
return YES;
}
}