What's the "correct" way of exactly placing and moving views when an app rotates? That is, how can I have fine-grained control of the position, size, and reflow of my views when the UI rotates from portrait to landscape orientation (or vice-versa)? I think my two options are:
- Use two superviews (portrait and landscape). On rotation: toggle between them.
- Use one superview. On rotation: change each subview's frame, bounds, and center properties.
If you have two views with distinct enough layouts and elements, then the first way might be good enough. If your two views are essentially the same thing sized for different orientations, the second way is probably a better way to do it using only one view.
I suspect the former could be done with IB and the latter should be done programmatically.
To have fine-grained control over the position and size of your subviews when the iPhone rotates, change the frame of the subviews in the UIViewController method willAnimateRotationToInterfaceOrientation:duration:
. This method is called within an animation block, so all the changes to your subviews' frames that you make inside of it are animated.
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
// Portrait frames
self.subviewA.frame = CGRectMake(x, y, width, height);
self.subviewB.frame = CGRectMake(x, y, width, height);
self.subviewC.frame = CGRectMake(x, y, width, height);
} else {
// Landscape frames
self.subviewA.frame = CGRectMake(x, y, width, height);
self.subviewB.frame = CGRectMake(x, y, width, height);
self.subviewC.frame = CGRectMake(x, y, width, height);
}
}
Here's one idea. I worry that the animations will work funny, but try it and see.
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toOrientation
duration:(NSTimeInterval)duration {
if (toOrientation == UIInterfaceOrientationLandscapeLeft ||
toOrientation == UIInterfaceOrientationLandscapeRight) {
[UIView beginAnimations:nil context:NULL]; {
[UIView setAnimationDuration:1.0];
}
[UIView setAnimationDelegate:self];
[viewA setFrame:CGRectMake(?,?,?,?)];
[viewB setFrame:CGRectMake(?,?,?,?)];
[viewC setFrame:CGRectMake(?,?,?,?)];
} [UIView commitAnimations];
} else if (toOrientation == UIInterfaceOrientationPortrait ||
toOrientation == UIInterfaceOrientationPortraitUpsideDown) {
[UIView beginAnimations:nil context:NULL]; {
[UIView setAnimationDuration:1.0];
}
[UIView setAnimationDelegate:self];
[viewA setFrame:CGRectMake(?,?,?,?)];
[viewB setFrame:CGRectMake(?,?,?,?)];
[viewC setFrame:CGRectMake(?,?,?,?)];
} [UIView commitAnimations];
}
I have an idea which is working properly for me where I am using a small view on a view where it rotates itself when orientation of device changes. Use one notifier then in the method change the orientation.
#define DegreesToRadians(x) (M_PI * x / 180.0)
.......
[[NSNotificationCenter defaultCenter] addObserver:showIndicator
selector:@selector(setProperOreintation)
name:UIDeviceOrientationDidChangeNotification
object:nil];
.......
-(void)setProperOrientation {
UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:0.3];
if (orientation == UIDeviceOrientationPortraitUpsideDown)
self.transform = CGAffineTransformRotate(CGAffineTransformIdentity, DegreesToRadians(180));
else if (orientation == UIDeviceOrientationLandscapeLeft)
self.transform = CGAffineTransformRotate(CGAffineTransformIdentity, DegreesToRadians(90));
else if (orientation == UIDeviceOrientationLandscapeRight)
self.transform = CGAffineTransformRotate(CGAffineTransformIdentity, DegreesToRadians(-90));
[UIView commitAnimations]; }
Here instead of rotate u can use translate or scale
There is a lot solutions for that. One of them is animations for controls as you read before. Another option is prepare 2 UIView one for portrait mode and another for landscape mode. And on switch orientation - load proper UIView. That's the way i used in my applications. Because sometimes differences between landscape and portrait is huge.
In simple cases is enough to set up correct autoresizingMask
for subviews. Read about it. It works really nice.