使用翻转转换创建新的导航堆栈,而不是推送另一个控制器

Posted

技术标签:

【中文标题】使用翻转转换创建新的导航堆栈,而不是推送另一个控制器【英文标题】:Creating a new navigation stack with a flip transition instead of pushing another controller 【发布时间】:2016-06-30 06:54:23 【问题描述】:

我有这段代码在 UITabControllers 之间进行翻转转换:

UIStoryboard *sb = [UIStoryboard storyboardWithName:@"OtherSb" bundle:nil];
PrimaryTabBarController *tabBarController = [sb  instantiateInitialViewController];

[UIView transitionWithView:[APP_DELEGATE window]
                  duration:0.8
                   options:UIViewAnimationOptionTransitionFlipFromLeft
                animations:^
                    [[APP_DELEGATE window] setRootViewController:tabBarController];
                    [[APP_DELEGATE window] makeKeyAndVisible];
                
                completion:nil];

然而,奇怪的是,在翻转过渡期间,标签栏会从屏幕底部短暂闪烁到顶部。我可以通过执行以下操作来停止:

PrimaryTabBarController *tabBarController = [sb  instantiateInitialViewController];

tabBarController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentViewController:tabBarController animated:YES completion:nil];

然而,这个问题是我将另一个视图控制器推到堆栈上,它可以轻松地在内存中运行。如何创建新的导航堆栈而不让标签栏弄乱动画?

【问题讨论】:

【参考方案1】:

rootViewController 不是您想象的非常具有动画效果的属性。 makeKeyAndVisible 也不是可动画的,您可能应该在运行任何动画之前这样做。

这个 API 本身已经很老了(ios 4.0),我个人认为它更像是 UIKit 的遗留物,自从 iOS 6 之后翻转视图仍然是一件事时,我还没有看到它被使用过。

iOS 7 中引入的自定义过渡是一种非常舒适的方式来制作任何类型的疯狂动画,但是正如您所注意到的,它创建了一个您并不总是需要的模态层次结构。

所有这些 API 都旨在与容器内的 sibling 视图一起使用。这是文档及其示例中提到的内容。而且 UIWindow 似乎不适合作为全局动画容器。

文档中的示例代码:

[UIView transitionWithView:containerView
           duration:0.2
           options:UIViewAnimationOptionTransitionFlipFromLeft
           animations:^ [fromView removeFromSuperview]; [containerView addSubview:toView]; 
           completion:NULL];

我建议您遵循与自定义过渡相同的逻辑,并首先设置虚拟根控制器,它将为您提供动画容器。

然后在其中添加视图或整个视图控制器,并使用

在兄弟视图之间运行动画
+ transitionFromView:toView:duration:options:completion:`

- transitionFromViewController:toViewController:duration:options:animations:completion:

+ transitionWithView:duration:options:animations:completion:

除此之外,还有一个有用的标志UIViewAnimationOptionShowHideTransitionViews 会自动隐藏翻转的视图以避免它在动画后闪烁或重新出现。

动画结束后,您可以一次调用整个根控制器,用户应该不会注意到这一点。

此 API 也有一些怪癖,例如,如果您在应用程序不在屏幕上时使用它,或者您在当前不可见的窗口上运行它,那么它会简单地吞下调用。我曾经有过类似的支票

if(fromViewController.view.window)  
    /* run animations */ 
 else  
    /* swap controllers without animations */ 

我做了一个示例项目来演示如何使用临时容器视图进行过渡

https://github.com/pronebird/FlipRootController

UIWindow 上的示例类别:

@implementation UIWindow (Transitions)

- (void)transitionToRootController:(UIViewController *)newRootController animationOptions:(UIViewAnimationOptions)options 
    // get references to controllers
    UIViewController *fromVC = self.rootViewController;
    UIViewController *toVC = newRootController;

    // setup transition view
    UIView *transitionView = [[UIView alloc] initWithFrame:self.bounds];

    // add subviews into transition view
    [transitionView addSubview:toVC.view];
    [transitionView addSubview:fromVC.view];

    // add transition view into window
    [self addSubview:transitionView];

    // flush any outstanding animations
    // UIButton may cancel transition if this method is called from touchUpInside, etc..
    [CATransaction flush];

    [UIView transitionFromView:fromVC.view
                        toView:toVC.view
                      duration:0.5
                       options:options
                    completion:^(BOOL finished) 
                        // set new root controller after animation
                        self.rootViewController = toVC;

                        // move VC's view out of transition view
                        [self addSubview:toVC.view];

                        // remove transition view
                        [transitionView removeFromSuperview];
                    ];


@end

【讨论】:

非常感谢,我会这样做的。我遇到的一个问题是我无法从容器视图内部的视图中调用容器视图上的翻转方法。应用程序的每一面都有一个复杂的视图层次结构。每当我转到新的视图控制器时,是否应该继续传递对 containerView 的引用?这似乎很麻烦。我应该做一个单身人士吗? @JohnFarkerson 这取决于你如何组织你的应用程序。您可以创建一个全局动画管理器,在动画期间将视图包装到容器视图中,然后在动画完成后交换根控制器并删除容器。 非常感谢您的详细解答!你无法想象这对我有多大帮助。我现在可以正常工作了!

以上是关于使用翻转转换创建新的导航堆栈,而不是推送另一个控制器的主要内容,如果未能解决你的问题,请参考以下文章

为什么我的导航控制器现在是以模态方式进行转换而不是推送?

没有导航控制器堆栈的视图控制器从一个到另一个的动画转换

在导航堆栈中以模态方式呈现视图控制器

推送导航控制器堆栈后视图不显示

从弹出控制器推送视图控制器

UINavigationController 推送带有隐藏导航栏的视图控制器