I basically want to have my subviews positioned differently depending upon the orientation of the iPad (Portrait or Landscape) using Sizing Classes introduced in xcode 6. I have found numerous tutorials explaining how different sizing classes are available for Iphones in portrait and landscape on the IB but however there seem to be none that cover individual landscape or portrait modes for the iPad on IB. Can anyone help?
Objective-c – Sizing class for iPad portrait and Landscape Modes
adaptive-uiios8objective csize-classes
Related Solutions
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.
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.
Best Answer
It appears to be Apple's intent to treat both iPad orientations as the same -- but as a number of us are finding, there are very legitimate design reasons to want to vary the UI layout for iPad Portrait vs. iPad Landscape.
Unfortunately, the current OS doesn't seem to provide support for this distinction ... meaning that we're back to manipulating auto-layout constraints in code or similar workarounds to achieve what we should ideally be able to get for free using Adaptive UI.
Not an elegant solution.
Isn't there a way to leverage the magic that Apple's already built into IB and UIKit to use a size class of our choosing for a given orientation?
~
In thinking about the problem more generically, I realized that 'size classes' are simply ways to address multiple layouts that are stored in IB, so that they can be called up as needed at runtime.
In fact, a 'size class' is really just a pair of enum values. From UIInterface.h:
So regardless of what Apple has decided to name these different variations, fundamentally, they're just a pair of integers used as a unique identifier of sorts, to distinguish one layout from another, stored in IB.
Now, supposing that we create an alternate layout (using a unused size class) in IB -- say, for iPad Portrait ... is there a way to have the device use our choice of size class (UI layout) as needed at runtime?
After trying several different (less elegant) approaches to the problem, I suspected there might be a way to override the default size class programmatically. And there is (in UIViewController.h):
Thus, if you can package your view controller hierarchy as a 'child' view controller, and add it to a top-level parent view controller ... then you can conditionally override the child into thinking that it's a different size class than the default from the OS.
Here's a sample implementation that does this, in the 'parent' view controller:
As a quick demo to see whether it worked, I added custom labels specifically to the 'Regular/Regular' and 'Compact/Regular' versions of the child controller layout in IB:
And here's what it looks like running, when the iPad is in both orientations:
Voila! Custom size class configurations at runtime.
Hopefully Apple will make this unnecessary in the next version of the OS. In the meantime, this may be a more elegant and scalable approach than programmatically messing with auto-layout constraints or doing other manipulations in code.
~
EDIT (6/4/15): Please bear in mind that the sample code above is essentially a proof of concept to demonstrate the technique. Feel free to adapt as needed for your own specific application.
~
EDIT (7/24/15): It's gratifying that the above explanation seems to help demystify the issue. While I haven't tested it, the code by mohamede1945 [below] looks like a helpful optimization for practical purposes. Feel free to test it out and let us know what you think. (In the interest of completeness, I'll leave the sample code above as-is.)