使用带有子类 UINavigationController 的自定义 iOS 7 过渡偶尔会导致黑屏
Posted
技术标签:
【中文标题】使用带有子类 UINavigationController 的自定义 iOS 7 过渡偶尔会导致黑屏【英文标题】:Using custom iOS 7 transition with subclassed UINavigationController occasionally results in black screen 【发布时间】:2014-08-26 18:58:47 【问题描述】:我将UINavigationController
子类化为我的应用程序的主根控制器。视图控制器的目的是呈现一个自定义启动屏幕,允许从(从右到左)滑动操作进入主应用程序。自定义转换在滑动期间淡化启动视图并显示下面的主应用程序(UITabBarController
)。我的大部分代码都遵循 objc.io 关于 ios7 中自定义视图控制器转换的文章:http://www.objc.io/issue-5/view-controller-transitions.html。
一切似乎都按预期工作。但是,很少有人报告说,一旦启动画面消失,就会出现黑屏,而不是主屏幕。我一直无法重现该行为。代码如下:
首先,在自定义应用委托中设置主根视图控制器:
- (void)applicationDidFinishLaunching:(UIApplication *)application
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.rootViewController = [[CustomRootViewController alloc] initWithNavigationBarClass:nil toolbarClass:nil];
self.window.rootViewController = self.rootViewController;
[self.window makeKeyAndVisible];
来自CustomRootViewController
的重要部分(注意我隐藏了导航栏):
- (instancetype) initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass
self = [super initWithNavigationBarClass:navigationBarClass toolbarClass:toolbarClass];
if (self)
self.navigationBarHidden = YES;
return self;
- (void)viewDidLoad
[super viewDidLoad];
self.delegate = self;
// This is the main UI for the app
UIStoryboard *mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
CustomTabBarControllerViewController *mainViewController = [mainStoryboard instantiateInitialViewController];
self.mainViewController = mainViewController;
UIStoryboard *splashStoryboard = [UIStoryboard storyboardWithName:@"Splash" bundle:[NSBundle mainBundle]];
SplashViewController *splashViewController = [splashStoryboard instantiateInitialViewController];
self.splashViewController = splashViewController;
// Initialize the navigation controller to have the main app sitting under the splash screen
self.viewControllers = @[self.mainViewController, self.splashViewController];
// In the public interface, called from the Splash screen when the user can perform the action
- (void)allowSwipeToDismiss
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)];
panGestureRecognizer.delegate = self;
[self.view addGestureRecognizer:panGestureRecognizer];
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
if (operation == UINavigationControllerOperationPop)
return [[CustomRootViewControllerAnimator alloc ] init];
return nil;
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController
return self.interactiveController;
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
// Only enable swiping when on the Swipe To Enter screen
// After we are in the main app, we don't want this gesture recognizer interfering with the rest of the app
if (self.topViewController == self.splashViewController)
return true;
else
return false;
- (void)handlePan:(UIPanGestureRecognizer *)recognizer
if (recognizer.state == UIGestureRecognizerStateBegan)
self.interactiveController = [[UIPercentDrivenInteractiveTransition alloc] init];
[self popViewControllerAnimated:YES];
else if (recognizer.state == UIGestureRecognizerStateChanged)
CGPoint translation = [recognizer translationInView:self.view];
CGFloat viewWidth = self.view.bounds.size.width;
CGFloat percentDone = -translation.x / viewWidth;
[self.interactiveController updateInteractiveTransition:percentDone];
else if (recognizer.state == UIGestureRecognizerStateEnded)
if (self.interactiveController.percentComplete < 0.5)
[self.interactiveController cancelInteractiveTransition];
else
[self.interactiveController finishInteractiveTransition];
self.interactiveController = nil;
这是我的自定义 UINavigationController 返回的 CustomRootViewControllerAnimator
的代码:
@implementation CustomRootViewControllerAnimator
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
return 1;
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
[[transitionContext containerView] insertSubview:toViewController.view
belowSubview:fromViewController.view];
[UIView animateWithDuration:[self transitionDuration:transitionContext] animations:^
CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
fromViewController.view.transform = CGAffineTransformMakeTranslation(-screenWidth, 0);
fromViewController.view.alpha = 0;
completion:^(BOOL finished)
fromViewController.view.transform = CGAffineTransformIdentity;
[transitionContext completeTransition:![transitionContext transitionWasCancelled]];
];
@end
【问题讨论】:
【参考方案1】:终于能够重现该问题,但仅限于 iOS 7.0。通过启动交互式过渡,然后取消它,我能够在下一次过渡开始时获得黑屏。在完成块中,我需要将 fromViewController 的 alpha 值设置回 1。同样,这仅在 iOS 7.0 中是必需的。它不会在 7.1 中发生。
【讨论】:
以上是关于使用带有子类 UINavigationController 的自定义 iOS 7 过渡偶尔会导致黑屏的主要内容,如果未能解决你的问题,请参考以下文章
带有使用 QAbstractTableModel 子类的 Qt 小部件的 MVC