So I have this iPhone app with a nested setup like this: [RootViewController]->[UITabBarController]->[UINavigationController]->[subViewControllers]. Everything is built programmatically, not in Interface Builder.
I'm simply trying to support all device orientations. All of my AutoResizing masks are set up and everything rotates and expands/contracts as necessary. Everything except for the NavigationController's navBar and Toolbar. (Normally, these views auto-resize automatically depending on the orientation. i.e. In landscape, the navbar and toolbar should automatically resize to about 32px, and 44px in Portrai mode.) What's peculiar is that the subView of the navigation controller is resizing, but the actual navBar and toolbar are not. In other words, the content stretches/shrinks about 12 px or so in whichever direction as though the navbar and toolbar resized. This appears to be the autoresizing masks doing their job.
So in order to attempt to solve this situation, I created a category for UINavigationController that responds to 'willRotateToInterfaceOrientation:duration:' in order to trigger the 'sizeToFit' selector. (This technique came from another post on Stack Overflow, with a similar issue.) I discovered, in my research, that only the outermost view controller receives this message, however, so I have my root view controller call this on tab controller, which in turn calls it on the nav controller, etc. This took care of that problem, and now my nested view controllers are being notified when the outer view is rotated. Using the sizeToFit technique, both the nav bar and toolbar are resizing on their own. However, that still leaves the issue of repositioning the toolbar, in order to make up for the offset from the size change. (Since our view coordinates in iPhone start at top left.)
What makes this tricky to do within the willRotate method, is that we have no way of knowing what the new bounding view dimensions will be, nor do we know what our current orientation is. (Unless you have deviceOrientationNotifications turned on, which I don't.) All we have to work with is what the new orientation will be, and what our toolbar's current frame values are. I could hard-code this and calculate frame adjustments the old-fashioned way using a piece of scratch paper, but I really want to retain the dynamic view sizing features without making any assumptions about the device's screen dimensions.
Therefore, using the following code, I managed to remedy this by simply 'bumping' the toolbar by 12px relative to the size it just became. In order to prevent it from over-bumping when the user flips the device, I use the current toolbar's frame height to derive the current orientation. (This means I still am making an assumption regarding the toolbar's before and after sizes, but I feel better about that since I can influence that to a degree as opposed to trying to programmatically resize the device's physical screen.)
@implementation UINavigationController (BLUINavigationControllerAutoRotate)
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
{
return TRUE;
}
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
// Resize the navBar
[[self navigationBar] performSelector:@selector(sizeToFit)
withObject:nil
afterDelay:(0.5f * duration)];
// Read our current frame size
CGRect toolbarFrame = self.toolbar.frame;
// Prevent over adjusting when flipping the device
if ((toolbarFrame.size.height < 38
&& (toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft
|| toInterfaceOrientation == UIInterfaceOrientationLandscapeRight))
|| (toolbarFrame.size.height > 38
&& (toInterfaceOrientation == UIInterfaceOrientationPortrait
|| toInterfaceOrientation == UIInterfaceOrientationPortraitUpsideDown)))
{
// We are already resized and repositioned.
return;
}
// Calculate and apply the toolbar offset
const NSInteger delta = 12; // the difference in size between 44 and 32 px.
toolbarFrame.origin.y += ((toInterfaceOrientation == UIInterfaceOrientationLandscapeLeft)
|| (toInterfaceOrientation == UIInterfaceOrientationLandscapeRight)) ? (delta) : (-delta);
self.toolbar.frame = toolbarFrame;
// Resize the toolbar
[[self toolbar] performSelector:@selector(sizeToFit)
withObject:nil
afterDelay:(0.5f * duration)];
}
@end
So, now I have properly behaved NavigationBar and Toolbar. The end-user will never know that I had to implement all of this extra work-around in order to re-enable what seems like is supposed to be a standard behavior. So my question is, why do I have to do all of this? Is there a better approach I should know about, or something that I simply am not doing elsewhere? (i.e. [navigationController setAutomaticallyResizesToolbarOnRotate:TRUE]; or something like that?) Like I said, this seems like it's supposed to be pre-wired, so for me to have to finagle with forwarding messages and forced triggers feels like an over-engineered hack rather than the proper solution.
Thanks!