直接看转场动画的协议时有点迷糊,做了一个简单的demo记录下理解过程
一、如何更改动画方式
站在自己的角度,最简单的理解是,要想制作一个自定义的转场动画,来避开原生的push和present的效果,则要更改系统默认动画的代理。
1.如果你使用的是navigationController的pushViewController方法,则要更改navigationController的delegate,并在delegate方法:
- (nullableid)navigationController:(UINavigationController*)navigationController animationControllerForOperation:(UINavigationControllerOperation)operation
fromViewController:(UIViewController*)fromVC
toViewController:(UIViewController*)toVC;
{
return[customerAnimator new];//返回一个自定义的动画执行者
}
2.如果你使用的是[currentVCpresentViewController:newVCanimated:YEScompletion:nil];来present新页面的话,则需要重新设置newVC的transitioningDelegate这个代理是实现了UIViewControllerTransitioningDelegate协议的对象并在delegate中实现协议的以下两个方法
- (nullableid)animationControllerForDismissedController:(UIViewController*)dismissed;
{
return[customerAnimator new];//返回一个自定义的动画执行者,在customerAnimator才实现真正的动画效果,此方法是这个页面dismiss的之后询问的代理方法,问动画由设来执行。
}
- (nullableid)animationControllerForPresentedController:(UIViewController*)presented presentingController:(UIViewController*)presenting sourceController:(UIViewController*)source;
{
return[customerAnimator new];//返回一个自定义的动画执行者,此方法在页面出现时询问,问出现的动画由谁来执行。
}
二、动画的实际效果怎么实现
从上面的协议方法中可以看出,协议最终都要求返回一个遵循UIViewControllerAnimatedTransitioning协议的对象,即customerAnimator,这个对象充当动画的执行者角色。
也就是说,这个执行者必须按照协议规定的样子去执行转场,这个animator必须实现的两个协议方法是:
//1.执行动画所需要的时间
- (NSTimeInterval)transitionDuration:(nullableid)transitionContext;
//2.转场动画效果具体实现在这个方法中去实现
- (void)animateTransition:(id)transitionContext;
所以可以看出,整个过程中核心是animator(动画的执行者)在起作用,核心方法是- (void)animateTransition:(id)transitionContext ,负责实现实际的效果实现
//简单的示例
- (void)animateTransition:(id)transitionContext {
//如何切换两个View,设置和动画写在此处
//toVC
UIViewController*toVC = [transitionContextviewControllerForKey:UITransitionContextToViewControllerKey];
//finally rect
CGRectfinallyRect = [transitionContextfinalFrameForViewController:toVC];
toVC.view.frame=CGRectOffset(finallyRect,0, [UIScreenmainScreen].bounds.size.height);
//3
[[transitionContextcontainerView]addSubview:toVC.view];
//4
[UIViewanimateWithDuration:[selftransitionDuration:transitionContext]delay:0.0options:UIViewAnimationOptionCurveLinearanimations:^{
toVC.view.frame= finallyRect;
}completion:^(BOOLfinished) {
[transitionContextcompleteTransition:YES];
}];
}
三、基于手势的转场动画
当在animator中实现了动画效果后,要想在手指向右滑动的过程中,动画也跟着手指的滑动程度去更新动画的执行程度,则需要实现一个代理方法,用于返回标志动画执行的百分比的实例。
同样,如果是使用UIviewController presentViewController 的方式 则是实现UIViewControllerTransitioningDelegate协议的方法
- (nullableid)interactionControllerForDismissal:(id)animator{
_persentDriver =[UIPercentDrivenInteractiveTransition new];
return_persentDriver;//此实例中纪录了动画执行的persent
}
//用于提供消失的时候的手势百分比
如果是使用navigationController的push方法将viewController推出的,则需要实现naviagtionControllerDelegate协议的
- (nullableid)navigationController:(UINavigationController*)navigationController interactionControllerForAnimationController:(id) animationController {
_persentDriver =[UIPercentDrivenInteractiveTransition new];
return_persentDriver;
}
代理方法
为了达到手在滑动的过程中动画跟着手指执行,需要在给viewontroller的view加一个滑动手势
UIPanGestureRecognizer*panGesture = [[UIPanGestureRecognizeralloc]initWithTarget:selfaction:@selector(handPanGesture:)];
[self.viewaddGestureRecognizer:panGesture];
- (void)hanPanGesture:(UIPanGestureRecognizer*)gesture {
CGPointpoint = [gesturetranslationInView:_viewController.view];
switch(gesture.state) {
caseUIGestureRecognizerStateBegan:
{
if(point.y>0) {
if(![self isBePresent])
//push进来的
[self.navigationControllerpopViewControllerAnimated:YES];
}else{
//present进来的
[selfdismissViewControllerAnimated:YEScompletion:^{
}];
}
}
} break; case UIGestureRecognizerStateChanged: { CGFloatpersent = point.y/200.0; [_persentDriverupdateInteractiveTransition:persent];//更新动画的百分比 } break; case UIGestureRecognizerStateCancelled: case UIGestureRecognizerStateEnded: { [_persentDriverfinishInteractiveTransition]; } break;
default:
break; } }
- (BOOL)isBePresented {
if(self.navigationController) {
NSArray*viewcontrollers=self.navigationController.viewControllers;
if(viewcontrollers.count>1) {
if([viewcontrollersobjectAtIndex:viewcontrollers.count-1]==self) {
//push方式
returnNO;
}
}else{
//present方式
returnYES;
}
}
returnNO;
}
将手势的执行和persentDriver数据更新链接起来后,UINaviagtionControllerDelegate和UIViewControllerTransitioningDelegate
协议中返回的_persentDriver才可以带有手势的执行比例,这样动画就可以跟着手指滑动的程度来更新了。