自定义转场动画,首先得确定要做什么样的动画
1.动画类
继承自NSObject,服从协议UIViewControllerAnimatedTransitioning
既然服从这个协议,那么这个协议里的方法就要实现
这个是用来确定动画时间的,可以在第二个协议方法中用到动画时间的时候用self来调用获取
// This is used for percent driven interactive transitions, as well as for container controllers that have companion animations that might need to
// synchronize with the main animation.
- (NSTimeInterval)transitionDuration:(nullableid <UIViewControllerContextTransitioning>)transitionContext;
这个是用来确定做什么样的转场动画
// This method can only be a nop if the transition is interactive and not a percentDriven interactive transition.
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext;
2.交互类
对于动画类方面push和present转场其实都是一样的
但是对于交互类,这两者可不太相同
先说说push,pop
继承自NSObject,要服从协议UINavigationControllerDelegate
在交互类的.m文件中定义两个私有属性,一个UINavigationController属性,一个UIPercentDrivenInteractiveTransition属性,前者是该交互类对应的导航栏,后者是用来实现根据手势的完成比例更新UI的
- (id<UIViewControllerAnimatedTransitioning>)navigationController:(UINavigationControlle *)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation fromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC {
/**
* 方法1中判断如果当前执行的是Pop操作,就返回我们自定义的Pop动画对象。
*/
if (operation ==UINavigationControllerOperationPop)
return [[PopAnimationalloc]init];
returnnil;
}
- (id<UIViewControllerInteractiveTransitioning>)navigationController:(UINavigationController *)navigationController
interactionControllerForAnimationController:(id<UIViewControllerAnimatedTransitioning>)animationController {
/**
* 方法2会传给你当前的动画对象animationController,判断如果是我们自定义的Pop动画对象,那么就返回interactivePopTransition来监控动画完成度。
*/
if ([animationControllerisKindOfClass:[PopAnimationclass]])
returnself.interactivePopTransition;
returnnil;
}
这两个是导航栏协议方法:第一个方法根据 operation的枚举值来确定返回push动画类,还是pop动画类第二个方法是根据animationController这个动画类的类型来返回服从了交互协议UIViewControllerInteractiveTransitioning的实例,而交互类的私有属性UIPercentDrivenInteractiveTransition正好就是一个服从该协议的类
除了协议之外,交互类还要实现pan手势触发的方法,在这个方法中做动画的更新UI的操作
/**
* 我们把用户的每次Pan手势操作作为一次pop动画的执行
*/
- (void)handleControllerPop:(UIPanGestureRecognizer *)recognizer {
/**
* interactivePopTransition就是我们说的方法2返回的对象,我们需要更新它的进度来控制Pop动画的流程,我们用手指在视图中的位置与视图宽度比例作为它的进度。
*/
CGFloat progress = [recognizertranslationInView:recognizer.view].x / recognizer.view.bounds.size.width;
/**
* 稳定进度区间,让它在0.0(未完成)~1.0(已完成)之间
*/
progress = MIN(1.0,MAX(0.0, progress));
if (recognizer.state ==UIGestureRecognizerStateBegan) {
/**
* 手势开始,新建一个监控对象
*/
self.interactivePopTransition = [[UIPercentDrivenInteractiveTransitionalloc]init];
/**
* 告诉控制器开始执行pop的动画
*/
[self.vcpopViewControllerAnimated:YES];
}
elseif (recognizer.state ==UIGestureRecognizerStateChanged) {
/**
* 更新手势的完成进度
*/
[self.interactivePopTransitionupdateInteractiveTransition:progress];
}
elseif (recognizer.state ==UIGestureRecognizerStateEnded || recognizer.state ==UIGestureRecognizerStateCancelled) {
/**
* 手势结束时如果进度大于一半,那么就完成pop操作,否则重新来过。
*/
if (progress >0.5) {
[self.interactivePopTransitionfinishInteractiveTransition];
}
else {
[self.interactivePopTransitioncancelInteractiveTransition];
}
self.interactivePopTransition =nil;
}
}
- (instancetype)initWithViewController:(UIViewController *)vc
{
self = [superinit];
if (self) {
self.vc = (UINavigationController *)vc;
self.vc.delegate =self;
}
returnself;
}
再说说如何使用
创建一个导航栏的子类
UIGestureRecognizer *gesture =self.interactivePopGestureRecognizer;
gesture.enabled =NO;
UIView *gestureView = gesture.view;
UIPanGestureRecognizer *popRecognizer = [[UIPanGestureRecognizeralloc]init];
popRecognizer.delegate =self;
popRecognizer.maximumNumberOfTouches =1;
[gestureView addGestureRecognizer:popRecognizer];
_navT = [[NavigationInteractiveTransitionalloc]initWithViewController:self];
[popRecognizer addTarget:_navTaction:@selector(handleControllerPop:)];
这个是需要注意的地方
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
/**
* 这里有两个条件不允许手势执行,1、当前控制器为根控制器;2、如果这个push、pop动画正在执行(私有属性)
*/
returnself.viewControllers.count !=1 && ![[selfvalueForKey:@"_isTransitioning"]boolValue];
}
说完了push,然后说说present,dismiss
动画类类似
说交互类
继承自NSObject,要服从协议 UIViewControllerTransitioningDelegate
(这里要说一下的是,交互类实例创建后,要把被模态出来的控制器赋值给交互类的属性)
初始化交互类
被模态出来的控制器的transitioningDelegate设置为该交互类实例
如果要支持手势滑动返回那么需要再创建一个继承自UIPercentDrivenInteractiveTransition的子类,并且以被模态出来的控制器初始化
注意手势是添加在被模态出来的控制器的view上的