Changing modal UIView's orientation to landscape mode on iPhone - iphone

Im having a modal view displayed on top of tab bar controller the orientation of tab bar controller and my modal view is currently in portrait mode, i need to only change my modal views orientation to landscape mode,
i Tried usign UIDevice Orientation but no success it only works when .plist has UIDeviceOrientationKey, and i dont want whole of my application in landscape mode. secondly i even tried using UIApplication setStatusBarOrientation but my view doesn't respond to it i.e. only my device(simulator) get rotated but the view still remains in portrait mode, using affine transformation on UIView creates an empty view my view is created through XIB.
Please Help
Thanks Rakesh

I have used this and it works well with some tweaking.

Until today I used the following solution:
-(void)setOrientation:(UIInterfaceOrientation)orientation
{
SEL rotation = #selector(_setRotatableViewOrientation:duration:);
NSMethodSignature * methodSignature = [window methodSignatureForSelector:rotation];
if (methodSignature)
{
NSInvocation * ivc = [NSInvocation invocationWithMethodSignature:methodSignature];
[ivc setTarget:window];
[ivc setSelector:rotation];
// arg0 will be self, 1 will be cmd_ (current method selector)
[ivc setArgument:&orientation atIndex:2];
double duration = 0.4;
[ivc setArgument:&duration atIndex:3];
[ivc invoke];
}
}
-(void)normalizeOrientation
{
UIDeviceOrientation dOrientation = [UIDevice currentDevice].orientation;
int orientation = -1;
switch(dOrientation)
{
case UIDeviceOrientationPortrait:
orientation = UIInterfaceOrientationPortrait;
break;
case UIDeviceOrientationPortraitUpsideDown:
orientation = UIInterfaceOrientationPortraitUpsideDown;
break;
case UIDeviceOrientationLandscapeRight:
orientation = UIInterfaceOrientationLandscapeLeft;
break;
case UIDeviceOrientationLandscapeLeft:
orientation = UIInterfaceOrientationLandscapeRight;
break;
}
if (orientation != -1 && orientation != tabBarController.interfaceOrientation)
[self setOrientation:orientation];
}
but the official AppStore wont accept new releases of my App due to
_setRotatableViewOrientation:duration:transition:toView:
isn't documented API.
So be aware for use it in your commercial projects!
Apple new automated API checker will reject your App!

Related

iOS7 / iOS6 Conditional Rotation Portrait / Landscape for different sections of App

Problem: A have an App that uses both Landscape mode (locked) and Portrait Mode (locked) for different parts of the app. Now I have a working solution however it doesn't seem correct and does have it's own problems.
Optimally I would love to force a orientation change. Thinking even about doing a view transformation if needed.
Basic flow of App:
HomeView (Portrait) (which has a few sub pushed views that are also portrait and locked to that).
LandscapeView (Landscape) (which has 5 pushed subviews that are also landscape)
Note:
HomeView has a link to LandscapeView
LandscapeView can go back to HomeView
At the end of the LandscapeView subviews it returns to the HomeView
Basic Image showing how this looks with the different view orientations. (The lines indicate flow of app, orientation of the images indicate how each screen should be )
Currently using the below implementation to call / set if the view is in portrait mode or landscape mode by [setLockedToPortait:YES] (for portrait view) etc.
This in term makes the query for what interface orientation to use from iOS if the device is rotated.
Now for the case of going to the LandscapeView, I show a temporary view over the top of the normal view asking to use to rotate their phone to landscape. (A temporary view is also shown when returning to the HomeView from a landscape view)
So once the user has rotated their device, it will trigger the correct orientation and then the temporary view will hide.
If the user then rotates their phone back to portrait at this point it will still be locked to landscape so will not trigger another view rotation (also no temp view will appear or anything)
Current Implementation Code::
// ---------------------- NavigationController (subclass of UINavigationController)
#interface NavigationController () {
BOOL isOrientationPortrait;
}
#end
#implementation NavigationController {
UIDeviceOrientation lastAccepted;
UIDeviceOrientation lastKnown;
}
-(void)setLockedToPortait:(BOOL)isLocked {
isOrientationPortrait = isLocked;
}
-(UIDeviceOrientation) getCurrentOrientation {
UIDeviceOrientation orientate = [[UIDevice currentDevice] orientation];
if(orientate == 0) { // needed for simulator
orientate = (UIDeviceOrientation)[UIApplication sharedApplication].statusBarOrientation;
}
return orientate;
}
// Deprecated in iOS6, still needed for iOS5 support.
- (BOOL)shouldAutorotateToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation
{
UIDeviceOrientation orientation = [self getCurrentOrientation];
[self setLastKnownOrientation:orientation];
if(isOrientationPortrait == YES) {
if([self isLastKnownPortrait] == YES) {
[self setLastAcceptedOrientation:orientation];
return YES;
} else {
return NO;
}
} else {
if([self isLastKnownLandscape] == YES) {
[self setLastAcceptedOrientation:orientation];
return YES;
} else {
return NO;
}
}
}
// iOS6/7 support
- (BOOL)shouldAutorotate
{
// find out the current device orientation
UIDeviceOrientation orientation = [self getCurrentOrientation];
[self setLastKnownOrientation:orientation];
return YES;
}
- (NSUInteger)supportedInterfaceOrientations
{
if(isOrientationPortrait == YES) {
if([self isLastKnownPortrait] == YES)
{
UIDeviceOrientation orientation = [self getCurrentOrientation];
[self setLastAcceptedOrientation:orientation];
}
return (UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown);
} else {
if([self isLastKnownLandscape] == YES)
{
UIDeviceOrientation orientation = [self getCurrentOrientation];
[self setLastAcceptedOrientation:orientation];
}
return (UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeRight );
}
}
-(void)setLastAcceptedOrientation:(UIDeviceOrientation)orient {
lastAccepted = orient;
}
-(void)setLastKnownOrientation:(UIDeviceOrientation)orient {
lastKnown = orient;
}
-(BOOL)isLastKnownPortrait {
return UIDeviceOrientationIsPortrait(lastKnown);
}
-(BOOL)isLastKnownLandscape {
return UIDeviceOrientationIsLandscape(lastKnown);
}
-(BOOL)isLastAcceptedPortrait {
return UIDeviceOrientationIsPortrait(lastAccepted);
}
-(BOOL)isLastAcceptedLandscape {
return UIDeviceOrientationIsLandscape(lastAccepted);
}
Current Problems:
Device rotations are always required after a view has loaded for the user going to Landscape mode from Portrait and vice versa.
If the user has the device orientation locked, this will not work at all.
When transitioning back from Landscape mode, and the user has already rotated their device to Portrait (in the last landscape view), the Portrait view's interface will be locked to a 'Landscape' layout until the user re-rotates their device (so currently I am just showing the overlay to rotate the device, but it is already rotated… very annoying for the user). Massive issue right now with the above implementation.
Would love to be able to:
Force an orientation change on the phone for the current view.
Set a preferred layout for a view which is forced between push/pops of views.
I've looked a lot at the other solutions on here and on the Apple Dev forums, however none seem to cover this problem, or still this orientation bug between the two views exists as well.
Thanks for any help or pointers! No advice will be discounted :D
--
Edit::
Solution Found thanks to #leo-natan!!
So instead of trying to force a change of orientation on the views. Just push a new modal view. This forces a change. You still need to above orientation code for managing rotations.
So what I have now in my HomeViewController:
LandscapeViewController * viewController = [[[LandscapeViewController ViewController alloc] init] autorelease];
UINib * nib = [UINib nibWithNibName:#"NavigationController" bundle:nil];
NavigationController *navController = [[nib instantiateWithOwner:nil options:nil] objectAtIndex:0];
[navController initWithRootViewController:viewController];
[self presentViewController:navController animated:YES completion:^{
// completion
}];
So it is necessary to re-add a new navigation controller for this modal view. Also note above 'presentViewController' is the new way of pushing Modal views.
Implemented this overloaded method for the managing of the view controller:
-(id)initWithRootViewController:(UIViewController *)rootViewController {
self = [super initWithRootViewController:rootViewController];
if(self){
}
return self;
}
Note: The above is not using storyboards. The problem may be solved by using storyboards and modally showing a view in the same fashion.
See my answer here, including a test project.
Basically, orientation can only be forced to change when presenting a view controller modally. For example, media playback in some apps. If you wish to transition from a view controller that can only be presented in portrait to a view controller that is only presented in landscape, you will need a modal presentation. Push will not work.

xcode - How do I keep views locked into portrait mode, but still allow one view to rotate?

I'm having a problem with device rotation. Except for ONE view, where I show a company splash screen, I want to lock all the remaining app views into Portrait display. In the project settings the supported orientations are Portrait and LandscapeLeft. In the 'Company Splash' it works fine and the view rotation is locked into LandscapeLeft no matter how I rotate the device. On all the other views when I rotate the device to the left the view alters instead of staying in portrait display. The methods are not even firing? If I remove Landscape left from the supported Orientations in the project, that screws up the 'Company Splash' view. I tried changing the shouldAutorotate return to NO, but that didn't help. Tried to work my way through the suggestions posted here, but that didn't help. If I put the following code into my AppDelegate.m, everything is locked into portrait mode and the 'Company Splash' crashes when accessed.
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskPortraitUpsideDown;
}
How do I lock the view into portrait mode no matter how the device is rotated except for the one screen?
** method from the 'Company Splash' view. Again, works like it is supposed to.
-(NSInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscapeLeft;
}
** methods from all the other views, which rotate out of portrait when I don't want them to
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// IOS5 Only returning that it should rotate to potrait
return (interfaceOrientation == UIDeviceOrientationPortrait);
}
-(BOOL)shouldAutorotate
{
// forcing the rotate IOS6 Only
return YES;
}
-(NSInteger)supportedInterfaceOrientations
{
// return number or enum IOS6 Only
return UIInterfaceOrientationMaskPortrait;
}
I thought maybe it might be because a UITabBarController is the root controller and I'm in a ViewController off of that? The methods are not even firing?
Add an observer to the viewDidLoad method of the view you want to rotate like this :
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter]
addObserver:self selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:[UIDevice currentDevice]];
and then set the views according the the landscape view inside the orientationChanged method like this :
- (void) orientationChanged:(NSNotification *)note{
UIDevice * device = [UIDevice currentDevice];
switch(device.orientation)
{
case UIDeviceOrientationPortrait:
break;
case UIDeviceOrientationPortraitUpsideDown:
break;
case UIDeviceOrientationLandscapeLeft:
break;
case UIDeviceOrientationLandscapeRight:
break;
default:
break;
};
}
Slap this method into the view controller you want locked in a certain orientation.
-(NSUInteger)supportedInterfaceOrientations
{
return UIInterfaceOrientationMaskLandscape;
}
just change to the orientation you want (the above locks it in landscape)
maybe u should allow all orientations, and lock portrait orientation in each class except company splash with
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (interfaceOrientation == UIInterfaceOrientationPortrait) {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
} else {
return (interfaceOrientation == UIInterfaceOrientationLandscapeRight);
}
}

iPad Interface Orientation Problem

When user tap on the UIView (which is fullscreen) my app needs to detect orientation and do some stuff. But there is one tiny problem.
If I start application in landscape mode and user tap background 'interfaceOrientation' variable is '0' and I don't know how to rearrange view elements. If I rotate simulator once everything is fine but if not 'interfaceOrientation' is '0'. What to do here?
UIInterfaceOrientation interfaceOrientation = [[UIDevice currentDevice] orientation];
if (interfaceOrientation == UIInterfaceOrientationPortrait || interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)
{
...
}
else if(interfaceOrientation == UIInterfaceOrientationLandscapeLeft)
{
...
}
else if(interfaceOrientation == UIInterfaceOrientationLandscapeRight)
{
...
}
I'm not sure how well that is solvable. UIDeviceOrientationUnknown is 0. This means, especially for the iPad which hasn't got a gyroscope, that the orientation at this time is simply not know. Imagine your iPad laying on a table, flat and the user is starting your application: there isn't any means to define that your application is actually running in landscape or portrait, unless you tilt the device accordingly. Hence.. the orientation at startup is always 0 (unknown).
You are casting a UIDeviceOrientation type to a UIInterfaceOrientation type. Device orientations have several different values beyond those of an interface orientation.
Try using:
UIDeviceOrientation deviceOrientation = [[UIDevice currentDevice] orientation];
switch (deviceOrientation) {
default:
case UIDeviceOrientationPortrait:
case UIInterfaceOrientationPortraitUpsideDown:
case UIDeviceOrientationUnknown:
//...
break;
case UIDeviceOrientationLandscapeLeft:
//...
break;
case UIDeviceOrientationLandscapeRight:
//...
break;
}
Edit: If the device orientation is unknown you should just set up your regular portrait view.
You should be able to access [UIApplication sharedApplication].statusBarOrientation if you can't access self.interfaceOrientation from your view controller.
Also, sometimes calling self.interfaceOrientation in viewDidLoad is risky because the view will load before it is aware of its orientation, so it will perform the rotation afterwards. In that case try finding it in viewWillAppear.
Or just override willRotateToInterfaceOrientation:duration and pass that information to the UIView that needs it. Note: that won't get called if you have a xib set to that orientation.

UIActionSheet orientation

I am having a problem trying to get the orientation of the device correct. I have to show an action sheet that should come depending on the orientation of the device. Here is the code I am using.
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
switch (orientation)
{
case UIDeviceOrientationPortrait:
case UIDeviceOrientationFaceUp:
case UIDeviceOrientationFaceDown:
case UIDeviceOrientationUnknown:
case UIDeviceOrientationPortraitUpsideDown:
[self displayActionSheetInPotraitMode];
break;
case UIInterfaceOrientationLandscapeLeft:
case UIInterfaceOrientationLandscapeRight:
[self displayActionSheetInLandscapeMode];
break;
default:
[self displayActionSheetInPotraitMode];
break;
}
Let's look what the UIDevice Class Reference tells us about the orientation property.
The value of this property always returns 0 unless orientation notifications have been enabled by calling beginGeneratingDeviceOrientationNotifications.
So, you should call
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications]
somewhere earlier in your code.
Also, I'd like to suggest a way to simplify your current code:
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
if (UIDeviceOrientationIsLandscape(orientation)) {
[self displayActionSheetInLandscapeMode];
} else {
[self displayActionSheetInPotraitMode];
}
See the answer here: UIActionSheet orientation
You should add a UIView (add it as subview to UIWindow) -> transform it to proper orientation and then add the UIActionSheet to this UIView.

iPhone, duplicate method message after hardware rotate?

I am pretty sure what I am seeing is how this is supposed to work, but I as just curious as to why. When I rotate the iPhone in the simulator the method (see below) that allows the orientation gets called twice with each single rotation. Is there a reason for this?
-(BOOL)shouldAutorotateToInterfaceOrientation:interfaceOrientation
EDIT_001
This is what gets called when the iPhone detects a rotation, I am just curious that each time I do a rotate in the simulator the NSLog statements print twice (i.e. the method is getting called twice)
-(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
BOOL autoRotate = NO;
switch(interfaceOrientation) {
case UIInterfaceOrientationPortrait:
NSLog(#"Orientation(%d): Portrait Supported", interfaceOrientation);
autoRotate = YES;
break;
case UIInterfaceOrientationPortraitUpsideDown:
NSLog(#"Orientation(%d): UpsideDown unsupported", interfaceOrientation);
autoRotate = NO;
break;
case UIInterfaceOrientationLandscapeLeft:
NSLog(#"Device: RIGHT, Interface: LEFT(%d)", interfaceOrientation);
autoRotate = YES;
break;
case UIInterfaceOrientationLandscapeRight:
NSLog(#"Device: LEFT, Interface: RIGHT(%d)", interfaceOrientation);
autoRotate = YES;
break;
}
return(autoRotate);
}
gary
Have you checked the arguments passed in to shouldAutorotateToInterfaceOrientation:? I have found it gets called one for horizontal, and once for vertical - basically to ask which orientations are OK, and then it's usually not called again.
If you are doing something in there you want done every time the device is rotated, I think it's a lot better to either use willRotateToInterfaceOrientation:duration: or listen for the rotation notifications (via UIDeviceOrientationDidChangeNotification), and leave shouldAutorotateToInterfaceOrientation: to flag what your app allows for rotation of that view controller.
This happens when the view is inside a tab controller, because the tab controller also calls that same method. Hope that helps.
-Oscar