This is more of a general question for people to provide me guidance on, basically Im learning iPad/iPhone development and have finally come across the multi-orientation sup
The iPhone SDK is built around having an MVC architecture, so in theory if you keep all your logic (model) separated from your UI (view) then you will only have to worry about the UI in one spot: your view controllers. For those, you could have a separate view controller for each orientation, each of which would then just be loaded with one if/else to choose which view controller to load.
The same idea holds for iPhone / iPad support, where you can load another view controller which can handle larger displays.
It really depends on what it is you are laying out.
If you look at the Apple Settings application, you can see that they use table views for the layout, with custom cells for most rows. With that, you can allow a simple interface to rotate pretty cheaply by just filling the width of the cells. This even applies to things like Mail, where there are edit text cells in each row. And tables can easily be all transparent, with only buttons or labels visible, so they do not look like tables.
You can get a lot of milage out of the autoresizingMask of every UIView. If you have one or more items that can have a flexible height, then you can usually get an interface layout that looks good in either orientation. Depending on how it looks, sometimes you can just pin everything to the top.
In rare cases, if all the interface elements fit in a square, you can just rotate them in place.
There are two times when you must explicitly handle orientation changes. One is when a view moves from beside to below another on rotation. The other is when you have different images for each orientation, for example if you always want to be full width.
There are sometimes ways to work around both of these. You might use stretchable images or limit yourself to one view per line. Or you might lock out orientation for certain views.
If you must change the layout of views, there is an explicit layoutSubviews method. You should try to handle all you conditional layout in this one method. It is only called when the view bounds change, for example on rotation or if you have made room for the keyboard. Make a custom view for each view hierarchy that needs to respond to rotation, and layout the subviews from there.
I do this with two simple methods in my view controller:
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
[self adjustViewsForOrientation:toInterfaceOrientation];
}
- (void) adjustViewsForOrientation:(UIInterfaceOrientation)orientation {
if (orientation == UIInterfaceOrientationLandscapeLeft || orientation == UIInterfaceOrientationLandscapeRight) {
titleImageView.center = CGPointMake(235.0f, 42.0f);
subtitleImageView.center = CGPointMake(355.0f, 70.0f);
...
}
else if (orientation == UIInterfaceOrientationPortrait || orientation == UIInterfaceOrientationPortraitUpsideDown) {
titleImageView.center = CGPointMake(160.0f, 52.0f);
subtitleImageView.center = CGPointMake(275.0f, 80.0f);
...
}
}
To keep this clean you could easily compartmentalize the view adjustments/reloading/etc. with methods called from inside the single if-else
conditional.
Refer the following link: http://mustafashaik.wordpress.com/2010/11/17/handling-orientations-in-ipad/
I can't vouch for this code, and in all honesty the above willRotateToInterfaceOrientation works great. Here's another take on it with FBDialog.m from Facebook for iphone / ipad. (albeit, I think this was for a webview)
here's the gist
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(deviceOrientationDidChange:)
name:@"UIDeviceOrientationDidChangeNotification" object:nil];
- (void)deviceOrientationDidChange:(void*)object {
UIDeviceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if ([self shouldRotateToOrientation:orientation]) {
CGFloat duration = [UIApplication sharedApplication].statusBarOrientationAnimationDuration;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:duration];
[self sizeToFitOrientation:YES];
[UIView commitAnimations];
}
}
-(CGAffineTransform)transformForOrientation {
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (orientation == UIInterfaceOrientationLandscapeLeft) {
return CGAffineTransformMakeRotation(M_PI*1.5);
} else if (orientation == UIInterfaceOrientationLandscapeRight) {
return CGAffineTransformMakeRotation(M_PI/2);
} else if (orientation == UIInterfaceOrientationPortraitUpsideDown) {
return CGAffineTransformMakeRotation(-M_PI);
} else {
return CGAffineTransformIdentity;
}
}
- (void)sizeToFitOrientation:(BOOL)transform {
if (transform) {
self.transform = CGAffineTransformIdentity;
}
CGRect frame = [UIScreen mainScreen].applicationFrame;
CGPoint center = CGPointMake(
frame.origin.x + ceil(frame.size.width/2),
frame.origin.y + ceil(frame.size.height/2));
CGFloat scale_factor = 1.0f;
if (FBIsDeviceIPad()) {
// On the iPad the dialog's dimensions should only be 60% of the screen's
scale_factor = 0.6f;
}
CGFloat width = floor(scale_factor * frame.size.width) - kPadding * 2;
CGFloat height = floor(scale_factor * frame.size.height) - kPadding * 2;
_orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsLandscape(_orientation)) {
self.frame = CGRectMake(kPadding, kPadding, height, width);
} else {
self.frame = CGRectMake(kPadding, kPadding, width, height);
}
self.center = center;
if (transform) {
self.transform = [self transformForOrientation];
}
}
In your question, you wrote:
I can just imagine so many issues with spaghetti code/thousands of "if" checks all over the place, that it would drive me nuts to make one small change to the UI arrangement.
One way to dodge this is to make a view hierarchy that splits the handling of iPhone/iPad specific changes from the very beginning. You'd only have to set which view is initially loaded for each device. Then you create a viewcontroller like you normally do, but you also subclass the viewcontroller you've created. One subclass for each device. That's where you can put the device specific code, like orientation handling. Like this:
MyViewController.h // Code that is used on both devices
MyViewController_iPhone.h // iPhone specific code, like orientation handling
MyViewController_iPad.h // iPad specific code, like orientation handling
If you are interested in this approach, I'd suggest that you read this article. It explains it in a very nice way.
One of the things the article mentions, is this:
--start of quote--
The beauty of this pattern is we don’t have to litter our code with crap that looks like this:
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// The device is an iPad running iPhone 3.2 or later.
// set up the iPad-specific view
} else {
// The device is an iPhone or iPod touch.
// set up the iPhone/iPod Touch view
}
---end of quote--
I hope that helps. Good luck!