转载

自定义转场动画

  • 在A控制器创建一个遵守 UIViewControllerTransitioningDelegate 协议的代理对象
  • 设置B的 transitioningDelegate 为步骤1中创建的代理对象
  • 在代理对象中实现相对应的方法,再方法中返回相对应的动画管理器
  • 调用 presentViewController:animated:completion: 并把参数 animated 设置为 true
  • 当转场的时候系统会从代理对象中实现的方法中寻找相对应得动画控制器,具体动画过程会在动画控制器中实现

具体步骤

  • 创建2个控制器,分别为A控制器和B控制器。
  • 创建B的 transitioningDelegate ,因为内容比较少,所以设置为A控制器本身 B.transitioningDelegate = self
  • 在A和B中实现控制器的切换,也就是
public func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?)

public func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?)
  • 使A遵守 TransitioningDelegate 并实现协议中的以下方法
// return:Present时的动画管理器
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {

let animator = TransitionAnimator()
// animator就是相对应的管理器,管理器的具体实现请往下看
animator.transition = .Present

return animator

}
// return:Dismiss时的动画管理器
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {

let animator = TransitionAnimator()

animator.transition = .Dismiss

return animator
}
  • TransitionAnimator 是遵守 UIViewControllerAnimatedTransitioning 的动画管理器,代理方法如下
// 转场动画持续的时间
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval{

return 2.0
}

// 转场动画具体的实现
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {

// present dismiss
// fromViewController A B
// toViewController B A

let fromViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)
toViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
// 转成动画中的容器View
let containerView = transitionContext.containerView()

let fromView = fromViewController?.view
let toView = toViewController?.view

// transition 是自己写的属性,用来分别是present和Dismiss
if self.transition == .Present{

presentAnimation(transitionContext, fromView: fromView!, toView: toView!, containerView: containerView!,fromController: fromViewController!, toController: toViewController!)

}else{

dismissAnimation(transitionContext, fromView: fromView!, toView: toView!, containerView: containerView!, fromController: fromViewController!, toController: toViewController!)
}


}
  • 再去实现present和dismiss相对应的动画即可
        // present 动画的实现
func presentAnimation(transitionContext:UIViewControllerContextTransitioning,fromView:UIView,toView:UIView,containerView:UIView,fromController:UIViewController,toController:UIViewController){

// 设置B控制器的锚点为右上角
toView.layer.anchorPoint = CGPoint(x: 1, y: 0)
// 可以获得B控制器最终的frame
toView.frame = transitionContext.finalFrameForViewController(toController)
// 将B控制器的View 添加的containerView上
containerView.addSubview(toView)

// 使B控制器逆时针旋转90度
toView.layer.transform = CATransform3DMakeRotation(-CGFloat(M_PI_2), 0, 0, 1)
toView.alpha = 0

let transitionDuration = self.transitionDuration(transitionContext)

// 弹性动画
UIView.animateWithDuration(transitionDuration, // 持续时间
delay: 0, // 延迟多久执行
usingSpringWithDamping: 0.6, // 弹性强度
initialSpringVelocity: 0, // 初始速度
options: .CurveLinear, // 弹性方案
animations: { () -> Void in

toView.alpha = 1
toView.layer.transform = CATransform3DIdentity

}) { (finished: Bool) -> Void in
//再完成之后,记得去实现转场动画结束的方法
let wasCancelled = transitionContext.transitionWasCancelled()
transitionContext.completeTransition(!wasCancelled)
}

}

func dismissAnimation(transitionContext:UIViewControllerContextTransitioning,fromView:UIView,toView:UIView,containerView:UIView,fromController:UIViewController,toController:UIViewController){

let transitionDuration = self.transitionDuration(transitionContext)

// 将A控制器的View添加到B控制器之下
containerView.insertSubview(toView, belowSubview: fromView)

UIView.animateWithDuration(transitionDuration * 0.5, animations: { () -> Void in

fromView.layer.transform = CATransform3DMakeRotation(-CGFloat(M_PI_2), 0, 0, 1)
fromView.alpha = 0

}) { (finished: Bool) -> Void in

let wasCancelled = transitionContext.transitionWasCancelled()
transitionContext.completeTransition(!wasCancelled)
}


}

这样转场动画就实现了

注意点:控制器的 modalPresentationStyle 也就是模态呈现风格模式是 Full Screen ,你也可以改成 custom 来自定义添加视图,不过当 stylecustom 的时候 fromView 不会被移除,而且在转成动画开始的时候要在动画管理中实现以下方法

_toVC.beginAppearanceTransition(true,animated:true)

func animationEnded(transitionCompleted: Bool) {
toVC.endAppearanceTransition()
}

支持手势的转场动画

  • 其实在present到B的时候会先执行代理的 interactionControllerForPresentation 这个方法来获得交互控制器,如果返回值为nil的话,才会执行刚刚写的动画,现在我们要使用到这个方法了
  • 首先新建一个类InteractionController,继承于 UIPercentDrivenInteractiveTransition ,在A中新建一个手势 var gesture:UIScreenEdgePanGestureRecognizer ,设置手势的 actionpresent 到B,然后实现代理方法,当用手势的时候返回刚刚新建的类,并把手势传给他

func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?{
// isPanGestureRecognizer是用来判断我是用手势还是用的按钮执行的present的动画
return self.isPanGestureRecognizer ? InteractionController.init(gesture: self.gesture) : nil

}
  • 在新建的类中新添加手势的方法
/// 当手势有滑动时触发这个函数
func gestureAction(gestureRecognizer: UIScreenEdgePanGestureRecognizer) {
switch gestureRecognizer.state {
case .Began: break
// 在此更新转成动画的进度,percentForGesture方法是自己写的用来计算进度的值,可以自行设置,具体内容请看demo
case .Changed: self.updateInteractiveTransition(self.percentForGesture(gesture)) //更新进度
case .Ended:
// 手势滑动超过5分之一的时候完成转场,否则取消转场动画
if self.percentForGesture(gestureRecognizer) >= 0.2 {
self.finishInteractiveTransition()
}else {
self.cancelInteractiveTransition()
}
default: self.cancelInteractiveTransition()

}
}
  • 这样的话手势滑动的功能就能实现了

自定义push和pop动画

  • 基本原理和刚刚的方法是一样的,不过需要设置 navigationControllerdelegate ,同时在代理中实现代理方法,也是返回动画管理器,在此就没有多写动画管理器,也是用的刚刚的动画器了。
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?{

if operation == .Push{
let animator = TransitionAnimator()

animator.transition = .Present

return animator
}else{
return nil
}

}

End

Github上的ColinEberhardt的 VCTransitionsLibrary 已经封装了一系列的VC自定义切换动画效果,可以在此基础上修改获得更炫酷的转场动画,Come On!

如果有时间的话可以自己试着做一下,同时希望各位的多多指导,欢迎互相交流

原文  http://chatwyn.ml/2016/02/14/自定义转场动画/
正文到此结束
Loading...