原文转自 https://www.jianshu.com/p/f4293785f1d5
UIViewControllerAnimatedTransitioning是iOS系统提供的转场动画协议,遵循该协议可自定义转场动画。
系统模态一个控制器时提供了模态风格的属性
分别是从底部滑入,水平翻转进入,交叉溶解以及翻页这四种风格,不受iPhone和iPad限制。
接手了一个需求是以push动画present一个控制器,系统提供的风格并不满足要求,只能动手改造转场动画。
忽略我的丑字。嘿。
过程:
1.控制A present控制器B
2.生成遵循UIViewControllerTransitioningDelegate协议的自定义TransitioningDelegate,将 A.transitioningDelegate = 自定义transitioningdelegate
3.自定义transitioningdelegate生成自定义animatedTransitioning
4.自定义animatedTransitioning遵循UIViewControllerAnimatedTransitioning协议,实现两个必要方法动画时长及核心动画。
话不多说,上代码
1.自定义转场动画代理
自定义transitioningdelegate.h 暴露了transitioningdelegate生成自定义animatedTransitioning的方法, targetEdge属性用来指定转场动画方向,可选UIRectEdgeTop,UIRectEdgeBottom,UIRectEdgeLeft,UIRectEdgeRight。用present实现push效果时使用UIRectEdgeRight。
使用自定义transitioningdelegate生成遵循转场动画协议的自定义类
2.实现转场动画核心动画方法
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
//源控制器
UIViewController *fromViewController = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
//目标控制器
UIViewController *toViewController = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
// Present:
// fromView = The presenting view.
// toView = The presented view.
// Dismiss:
// fromView = The presented view.
// toView = The presenting view.
//容器视图
UIView *containerView = transitionContext.containerView;
UIView *fromView = nil;
UIView *toView = nil;
if ([transitionContext respondsToSelector:@selector(viewForKey:)])
{
//iOS8之后
fromView = [transitionContext viewForKey:UITransitionContextFromViewKey];
toView = [transitionContext viewForKey:UITransitionContextToViewKey];
}
else
{
//iOS7
fromView = fromViewController.view;
toView = toViewController.view;
}
BOOL isPresenting = (toViewController.presentingViewController == fromViewController);
CGRect fromFrame = [transitionContext initialFrameForViewController:fromViewController];
CGRect toFrame = [transitionContext finalFrameForViewController:toViewController];
// Based on our configured targetEdge, derive a normalized vector that will
// be used to offset the frame of the presented view controller.
__block CGVector offset;
if (self.targetEdge == UIRectEdgeTop)
offset = CGVectorMake(0.f, 1.f);
else if (self.targetEdge == UIRectEdgeBottom)
offset = CGVectorMake(0.f, -1.f);
else if (self.targetEdge == UIRectEdgeLeft)
offset = CGVectorMake(1.f, 0.f);
else if (self.targetEdge == UIRectEdgeRight)
offset = CGVectorMake(-1.f, 0.f);
else
NSAssert(NO, @"targetEdge must be one of UIRectEdgeTop, UIRectEdgeBottom, UIRectEdgeLeft, or UIRectEdgeRight.");
if (isPresenting)
{
// For a presentation, the toView starts off-screen and slides in.
fromView.frame = fromFrame;
toView.frame = CGRectOffset(toFrame, toFrame.size.width * offset.dx * -1,
toFrame.size.height * offset.dy * -1);
}
else
{
fromView.frame = fromFrame;
toView.frame = toFrame;
}
// We are responsible for adding the incoming view to the containerView
// for the presentation.
if (isPresenting)
{
[containerView addSubview:toView];
}
else
{
// -addSubview places its argument at the front of the subview stack.
// For a dismissal animation we want the fromView to slide away,
// revealing the toView. Thus we must place toView under the fromView.
[containerView insertSubview:toView belowSubview:fromView];
}
NSTimeInterval transitionDuration = [self transitionDuration:transitionContext];
//动画
[UIView animateWithDuration:transitionDuration animations:^{
if (isPresenting)
{
toView.frame = toFrame;
}
else
{