关于转场动画之Nav转场

视图控制器转换(View Controller Transition) 俗称 关于转场动画

UINavigationController 的 push pop 转场动画

视图控制器中的视图显示在屏幕上有两种方式:最主要的方式是内嵌在容器控制器中,比如 UINavigationController,UITabBarController, UISplitController;由另外一个视图控制器显示它,这种方式通常被称为模态(Modal)显示。View Controller Transition 是什么?在 NavigationController 里 push 或 pop 一个 View Controller,在 TabBarController 中切换到其他 View Controller,以 Modal 方式显示另外一个 View Controller,这些都是 View Controller Transition。
首先放个官方链接以示正式
官方支持的可以自定义的转场动画
  • UINavigationController 的 push pop 转场动画
  • UIViewController 的 present dissmiss 转场动画。仅限于modalPresentationStyle属性为 UIModalPresentationFullScreen 或 UIModalPresentationCustom 这两种模式
  • UITabBarController 中切换 Tab 转场动画
  • UICollectionViewController 的布局转场 UICollectionViewController 与 UINavigationController 结合的转场方式

Push & Pop

首先介绍一下系统方法:
  1. 主要会涉及到两个UINavigationController的代理
    解释下fromVC & toVC —-> 如果是从A视图控制器push到B,则A是fromVC,B是toVC。从B视图控制器pop到A时,B变成了fromVC,A是toVC

    1
    2
    3
    4
    5
    6
    7
    8
    9
    看返回参数 这是个返回动画的代理
    - (nullable id <UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationController *)navigationController
    animationControllerForOperation:(UINavigationControllerOperation)operation
    fromViewController:(UIViewController *)fromVC
    toViewController:(UIViewController *)toVC NS_AVAILABLE_IOS(7_0);
    看返回参数 这是个交互代理 就是手势驱动了
    - (nullable id <UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
    interactionControllerForAnimationController:(id <UIViewControllerAnimatedTransitioning>) animationController NS_AVAILABLE_IOS(7_0);
  2. 既然动画是 返回的id类型的类 那我们看下 这个代理是什么

    1
    2
    3
    4
    返回动画执行时间
    - (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext;
    关于自定义动画就要在这里操作了
    - (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
  3. 再看下交互代理有什么 UIViewControllerInteractiveTransitioning

    1
    2
    3
    4
    5
    6
    7
    8
    系统有这个类是遵循这个交互代理的 我们只要继承这个类就行了
    NS_CLASS_AVAILABLE_IOS(7_0) @interface UIPercentDrivenInteractiveTransition : NSObject <UIViewControllerInteractiveTransitioning>
    关于这个类的几个重要的方法
    - (void)pauseInteractiveTransition NS_AVAILABLE_IOS(10_0);
    - (void)updateInteractiveTransition:(CGFloat)percentComplete;
    - (void)cancelInteractiveTransition;
    - (void)finishInteractiveTransition;
至此 所有用到的类及方法简单的介绍了 Let’s do it

我的实现目的是为了省化系统的一堆代理,就算你不知道有哪些代理一样能自定义转场动画。

关于UINavigationController的处理:可以在自定义的baseNav里做处理 也可以建个UINavigationController的Category
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
利用runtime 做个方法替换
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method sysPush = class_getInstanceMethod(self.class, @selector(pushViewController:animated:));
Method dfPush = class_getInstanceMethod(self.class, @selector(df_pushViewController:animated:));
method_exchangeImplementations(sysPush, dfPush);
});
}
目的的话当然是设置代理了 要不然 动画怎么走
- (void)df_pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
// 为了适用一些情况 考虑之后 这里的判断我去掉了
// if ([viewController conformsToProtocol:@protocol(DFTransitionProtocol)]) {
self.delegate = (id<UINavigationControllerDelegate>)viewController;
// }
[self df_pushViewController:viewController animated:animated];
}
关于UIViewController的处理:可以在自定义的baseVC里做处理 也可以建个UIViewController的Category (初始想法是把nav的代理指定到自定义的NSObject的,不在UIViewController的Category里面操作的。也不知道为什么就没按意愿走)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
这里的操作除了实现Nav的两个代理之外还要做些操作
nav的push里面虽然指定了代理 但是pop回来的时候代理就丢了 解决方案2选1: 1:替换nav的pop方法重新指定代理 2:替换vc的appear方法重新指定代理
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method sysdidDisappear = class_getInstanceMethod(self.class, @selector(viewDidAppear:));
Method dfdidDisappear = class_getInstanceMethod(self.class, @selector(df_viewDidAppear:));
method_exchangeImplementations(sysdidDisappear, dfdidDisappear);
});
}
- (void)df_viewDidAppear:(BOOL)animated {
self.navigationController.delegate = self;
[self df_viewDidAppear:animated];
}
关于动画 @interface DFBaseTransitionAnimation : NSObject

只要继承这个类重写动画的两个方法 就可以实现自己的自定义动画了

关于手势驱动类 @interface DFInteractiveTransition : UIPercentDrivenInteractiveTransition 里面都做了处理了 详细就看工程吧 最后会附上工程demo

关于使用

使用很简单 只要vc 遵守这个协议 实现方法就行

自定义动画要继承自 DFBaseTransitionAnimation

如果不实现 那么返回的是DFBaseTransitionAnimation 这个里面的默认动画
手势的话也是默认手势 详情看协议

1
2
- (DFBaseTransitionAnimation *)pushTransitionAnimation;
- (DFBaseTransitionAnimation *)popTransitionAnimation;

最后说下坑

首先说下vc返回释放不了的问题,起初以为是delegate造成的没释放,后来发现是因为手势里面对VC进行了强持有引起的

系统nav有个返回的手势 重写代理之后就不能用了 不知道应该是这样的还是我写的有错误的问题

动画里面 用UIView的动画转场 可以手势百分比 当用CATransition 做layer动画的时候 返回手势就不能百分比

github-demo