UITabBarController, MoreNavigationController and the Holy Grail of Device Rotation

别来无恙 提交于 2019-11-30 02:30:18

Apple advises against subclassing UITabBarController, so I found an easy way to handle autorotation using categories instead. It doesn't fix your bug with the More... view controllers, but I think it's a more Apple-friendly way of getting the job done (and means less subclassing for you).

To make every tab in my application autorotate properly, I've defined -shouldAutorotateToInterfaceOrientation: in my custom view controllers, but they are all inside UINavigationControllers within a UITabBarController, so the message won't get sent down the chain to my VC until those two also respond. So I added the following lines to my app delegate files:

Added to the bottom of MyAppDelegate.h

@interface UITabBarController (MyApp)
@end

@interface UINavigationController (MyApp)
@end

Added to the bottom of MyAppDelegate.m

@implementation UITabBarController (MyApp) 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}
@end

@implementation UINavigationController (MyApp) 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    return YES;
}
@end

It appears we have a bug. I have created a reproducible, minimal test case, and reported it via Apple Bug Reporter (RADAR Problem 7139857).

Update: This has been fixed as of iPhone OS 3.1.

The essential problem is:

View controllers already on a Navigation Controller stack do not receive willAnimateRotationToInterfaceOrientation:duration: messages when a Tab Bar Controller's 'More Navigation Controller' is in use.

This problem does not occur when the tab bar item view controllers are basic view controllers. Only when they are navigation controllers and only when the "More" navigation hierarchy is in use.

The console message (regarding two-stage rotation animation) suggests that something within the framework (the More Navigation Controller?) is still using a two-stage animation, even though single stage is now recommended as of iPhone OS 3.0.

That could explain why willAnimateRotationToInterfaceOrientation: is not called in that particular case. Per Apple's view controler documentation, this message will NOT be invoked when two-stage, first/second-half orientation messages are being responded to instead.

mmlac

A slightly modified version of Victorb's answer which allows every single view controller to decide if it allows rotation.

Here as a gist for easier copying & forking

AppDelegate.h

@interface UITabBarController (MyApp)
@end

@interface UINavigationController (MyApp)
@end

AppDelegate.m

@implementation UITabBarController (MyApp) 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    UIViewController *selectedVC = [self selectedViewController];
    if ([selectedVC respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
        return [selectedVC shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
    }

    //optimistic return - if you want no rotation, you have to specifically tell me!
    return YES;
}
@end

@implementation UINavigationController (MyApp) 
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation {
    UIViewController *visibleVC = [self visibleViewController];
    if ([visibleVC respondsToSelector:@selector(shouldAutorotateToInterfaceOrientation:)]) {
        return [visibleVC shouldAutorotateToInterfaceOrientation:toInterfaceOrientation];
    }

    //optimistic return - if you want no rotation, you have to specifically tell me!
    return YES;
}
@end
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!