Supporting two orientations with separate view controllers in iOS - objective-c

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
}
}

Related

iPad Landscape orientation

I am creating an App in portrait and Landscape Mode for iPad . I create a XIB in Landscape Mode for iPad but when i run that App it always shows in portrait Mode .
I set all setting under property list(.plist) file as "Supported Interface orientation(iPad)" and set Landscape(Left Home button) and landscape(Right Home Button) and also check the Orientation from the Code but all this doesn't work.
please help us if any one knows the exact problem or this , we are using Navigation Controller
Add this line of code in didFinishLaunchingWithOptions in delegate.
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(preferredInterfaceOrientationForPresentation) name:#"UIDeviceOrientationDidChangeNotification" object:nil];
Then add following methods in delegate first and run the app, if fixed, COOOOL else add following four methods in each your view controller. You problem will be fixed.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return (interfaceOrientation == UIInterfaceOrientationLandscapeLeft);
}
- (BOOL)shouldAutorotate {
return NO;
}
- (NSUInteger)supportedInterfaceOrientations {
return UIInterfaceOrientationMaskLandscape;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation {
return UIInterfaceOrientationLandscapeLeft;
}

iOS7 Autoresize When Switching Views Programmatically

I have googled for a few hours with no luck, so I'm coming to you guys to save me here!
Apparently, I can't find the right information on how exactly to go about doing this (or the best way). I have an app that supports portrait and landscape (no support for upside down though). However, the portrait and landscape views are COMPLETELY different, so I'll need to use two views to represent each. Am I correct in assuming I need 3 viewcontrollers in my storyboard (the main one, and then one for portrait and one for landscape? I was going to use just two but I didn't see how to if I start with the portrait, and then need to load landscape, I would have to delete portrait, which is where my code is?
My viewcontroller has the correct constraints in place to keep the label top center, but when replacing or swapping the views programmatically, it seems the auto-resize doesn't get called. I finally fixed this by resetting the frames on the subviews, but now when the device is flipped upside down, the portrait label is forever shifted to the right. So I'd just like to know the proper way to do this, as I'm sure this can't be it.
As far as code, I have one obj-c viewcontroller class with the following modified methods...
#interface AMBViewController ()
#property (strong, nonatomic) UIViewController *portraitViewController;
#property (strong, nonatomic) UIViewController *landscapeViewController;
#end
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
UIApplication *app = [UIApplication sharedApplication];
UIInterfaceOrientation currentOrientation = app.statusBarOrientation;
[self doLayoutForOrientation:currentOrientation];
}
-(void) willAnimateRotationToInterfaceOrientation:
(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self doLayoutForOrientation:toInterfaceOrientation];
}
-(void) doLayoutForOrientation:(UIInterfaceOrientation)orientation {
if (UIInterfaceOrientationIsPortrait(orientation)) {
self.portraitViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Portrait"];
if (self.landscapeViewController != nil ) {
[self.landscapeViewController.view removeFromSuperview];
self.landscapeViewController = nil;
}
self.portraitViewController.view.frame = self.view.bounds;
[self.view insertSubview:self.portraitViewController.view atIndex:0];
} else {
self.landscapeViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"Landscape"];
if (self.portraitViewController != nil ) {
[self.portraitViewController.view removeFromSuperview];
self.portraitViewController = nil;
}
self.landscapeViewController.view.frame = self.view.bounds;
[self.view insertSubview:self.landscapeViewController.view atIndex:0];
}
}
Just to be clear on my storyboard, I have one blank root controller (subclass AMBViewController) and two other view controllers "Landscape" and "Portrait"
I might also mention that the label only gets off in portrait view IF you rotate the device in a full circle (4 right or 4 left rotations). If you go right right (now it's upside down) but then left left, it's still fine. It's only when the screen flips from Right/Left Landscape to Left/Right Landscape that it messes up. Really weird, I know I must be omitting something important.
Any help is greatly appreciated. Thanks!
Solution: After finding a guide located on Apple's Dev Site (finally) I was able to come up with a solution using segues and a modal window. First view controller is portrait, second view controller is landscape, connected by a modal segue. The first view controller has the following modified methods:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
_isShowingLandscapeView = NO;
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(orientationChanged:)
name:UIDeviceOrientationDidChangeNotification
object:nil];
}
-(void) orientationChanged:(NSNotification *)notification {
UIDeviceOrientation orientation = [UIDevice currentDevice].orientation;
if (UIInterfaceOrientationIsPortrait(orientation) && _isShowingLandscapeView
&& orientation != UIInterfaceOrientationPortraitUpsideDown) {
[self dismissViewControllerAnimated:NO completion:nil];
_isShowingLandscapeView = NO;
} else if (UIInterfaceOrientationIsLandscape(orientation) && !_isShowingLandscapeView ) {
[self performSegueWithIdentifier:#"ShowLandscape" sender:self];
_isShowingLandscapeView = YES;
}
}
Thanks to all who might have looked into this!

iOS5,presentModalViewController presenting new view incorrectly

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];

iOS: Change interface orientation when pop view controller

All view controllers in my app are working only in portrait orientation except one which can be portrait or landscape oriented.
I have some usage scenario like following:
I push controller which works in both orientations to UITabBarController
User change orientation from portait to landscape
User press "back button"
After these actions application remains in landscape orientation and does not change it automatically to portrait.
I control view controller orientation using supportedInterfaceOrientations (I use iOS 6.0). What I do wrong? How can I get correct behaviour when application automatically change orientation to allowed when user press back button? Thank you for answer!
In iOS 6 (and possibly earlier), if a view controller is offscreen when the device rotates, it does not get any notification. Nor does it get sent willAnimateRotationToInterfaceOrientation:duration: when it becomes the top view controller.
You need to keep track of the current orientation of the view controller and check the device orientation in viewWillAppear:. If they are different, you can use willAnimateRotationToInterfaceOrientation:duration: to set it correctly.
Since this is something you are likely to do a lot, you may want to create a generic superclass that your view controllers inherit from.
A typical solutions is:
#implementation MyHandlesOffscreenRotationController
{
BOOL isShowingPortrait;
}
- (void) viewDidLoad
{
[super viewDidLoad];
isShowingPortrait = UIInterfaceOrientationIsPortrait(
[[UIApplication sharedApplication] statusBarOrientation]);
}
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
BOOL currIsPortrait = UIInterfaceOrientationIsPortrait(
[[UIApplication sharedApplication] statusBarOrientation]);
if ((isShowingPortrait && !currIsPortrait) ||
(!isShowingPortrait && currIsPortrait)) {
[self willAnimateRotationToInterfaceOrientation:
[[UIApplication sharedApplication] statusBarOrientation]
duration:0.0f];
}
}
#end
Just override -(BOOL)shouldAutoRotate and - (NSUInteger)supportedInterfaceOrientations inside a UINavigationController category, then ViewController will force rotate to its supported orientation after pop from other ViewController.
#implementation UINavigationController (Rotate)
- (BOOL)shouldAutorotate
{
return [self.topViewController shouldAutorotate];
}
- (NSUInteger)supportedInterfaceOrientations
{
return [self.topViewController supportedInterfaceOrientations];
}
#end
iOS 9 and above
At the time of pop just write the below-mentioned code in your viewWillAppear method.
[[UIDevice currentDevice] setValue:[NSNumber numberWithInteger: UIInterfaceOrientationPortrait]forKey:#"orientation"];
With this, your view will appear in portrait mode.
P.L. has a nice solution in this topic: Present and instantly dismiss an empty modal view controller which allows only portrait | landscape
How to change the device orientation programmatically in iOS 6

iPad app with multiple views, how do I make ONE single view viewable in landscape only?

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.