Reputation: 414
My app has four tabs: A, B, C and D. Their UIViewController
are managed by UITabBarController
. The app supports rotation, and so each view controller returns YES
to shouldAutorotateToInterfaceOrientation
.
Using springs and struts, most of the rotation is done automatically by iOS. However, tab A also requires further positioning, and it is done in its VC's willRotateToInterfaceOrientation
method.
When the VC for tab A is selected and the screen is rotated, that VC receives a willRotateToInterfaceOrientation
message (propagated by iOS from UITabBarController
), and the resulting rotation is correct.
However, when the selected tab is B and the screen is rotated, A's willRotateToInterfaceOrientation
is not called. Makes sense. But if I then select tab A, I get only the results of applying its springs and struts, without the post-processing done by its willRotateToInterfaceOrientation
.
After struggling with this for a while, and after failing to find a solution online, I came up with the following. I subclassed UITabBarController
and in its willRotateToInterfaceOrientation
I call all the VCs' willRotateToInterfaceOrientation
regardless of which one is the selectedViewController
:
- (void) willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
if (self.viewControllers != nil) {
for (UIViewController *v in self.viewControllers)
[v willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
}
It works, but it looks like a hack, and my question is whether I was doing the right thing. Is there a way to tell iOS to always call a VC's willRotateToInterfaceOrientation
before displaying it for the first time after a screen rotation?
Upvotes: 4
Views: 783
Reputation: 385890
The best way to handle custom layout is by subclassing UIView
and overriding the layoutSubviews
method. The system sends layoutSubviews
to a view whenever its size is changed (and at other times). So when your view A is about to appear on screen with a different size (because the interface was rotated while view B was on screen), the system sends view A a layoutSubviews
message, even though it doesn't send view controller A a willRotateToInterfaceOrientation:
message.
If you are targeting iOS 5.0 or later, you can override the viewDidLayoutSubviews
method of your UIViewController
subclass and do your layout there, instead of subclassing UIView
. I prefer to do it in my view's layoutSubviews
, to keep my view-specific logic separate from my control logic.
It's also a bad idea to do layout in willRotateToInterfaceOrientation:
because the system sends that message before actually changing the size of the view, and before the rotation animation block. It sends the willAnimateRotationToInterfaceOrientation:duration:
, layoutSubviews
, and viewDidLayoutSubviews
messages inside the rotation animation block, so the repositioning of your subviews will be animated if the view is on screen during the rotation.
Upvotes: 4