使用翻转过渡创建新的导航堆栈而不是推送另一个控制器
Creating a new navigation stack with a flip transition instead of pushing another controller
我用这段代码在 UITabController 之间进行翻转转换:
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];
然而,这个问题是我将另一个视图控制器推到堆栈上,它可以很容易地 运行 通过内存。如何创建新的导航堆栈而不让标签栏弄乱动画?
rootViewController
并不像您想象的那样可动属性。 makeKeyAndVisible
也不是可动画的,您可能应该在 运行 任何动画之前执行此操作。
这个 API 本身已经很老了 (iOS 4.0),我个人认为它更像是 UIKit 的遗留物,自 iOS 6 以来我就没见过它被使用当翻转视图仍然是一件事时。
iOS7 中引入的自定义过渡是制作任何疯狂动画的一种非常舒适的方式,但是正如您所注意到的,它创建了一个您并不总是需要的模式层次结构。
所有这些 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 也有一些怪癖,例如,如果您在应用程序不在屏幕上时偶然使用它,或者您 运行 在不在屏幕上的 window 上使用它当前可见,那么它将简单地吞下呼叫。我曾经有过像
这样的支票
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
我用这段代码在 UITabController 之间进行翻转转换:
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];
然而,这个问题是我将另一个视图控制器推到堆栈上,它可以很容易地 运行 通过内存。如何创建新的导航堆栈而不让标签栏弄乱动画?
rootViewController
并不像您想象的那样可动属性。 makeKeyAndVisible
也不是可动画的,您可能应该在 运行 任何动画之前执行此操作。
这个 API 本身已经很老了 (iOS 4.0),我个人认为它更像是 UIKit 的遗留物,自 iOS 6 以来我就没见过它被使用当翻转视图仍然是一件事时。
iOS7 中引入的自定义过渡是制作任何疯狂动画的一种非常舒适的方式,但是正如您所注意到的,它创建了一个您并不总是需要的模式层次结构。
所有这些 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 也有一些怪癖,例如,如果您在应用程序不在屏幕上时偶然使用它,或者您 运行 在不在屏幕上的 window 上使用它当前可见,那么它将简单地吞下呼叫。我曾经有过像
这样的支票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