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?
The best way to handle custom layout is by subclassing
UIViewand overriding thelayoutSubviewsmethod. The system sendslayoutSubviewsto 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 alayoutSubviewsmessage, even though it doesn’t send view controller A awillRotateToInterfaceOrientation:message.If you are targeting iOS 5.0 or later, you can override the
viewDidLayoutSubviewsmethod of yourUIViewControllersubclass and do your layout there, instead of subclassingUIView. I prefer to do it in my view’slayoutSubviews, 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 thewillAnimateRotationToInterfaceOrientation:duration:,layoutSubviews, andviewDidLayoutSubviewsmessages inside the rotation animation block, so the repositioning of your subviews will be animated if the view is on screen during the rotation.