UIViewControllerTransitioningDelegate
协议的代理对象 transitioningDelegate
为步骤1中创建的代理对象 presentViewController:animated:completion:
并把参数 animated
设置为 true
transitioningDelegate
,因为内容比较少,所以设置为A控制器本身 B.transitioningDelegate = self
public func presentViewController(viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)?)
public func dismissViewControllerAnimated(flag: Bool, completion: (() -> Void)?)
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 动画的实现
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
来自定义添加视图,不过当 style
为 custom
的时候 fromView
不会被移除,而且在转成动画开始的时候要在动画管理中实现以下方法
_toVC.beginAppearanceTransition(true,animated:true)
和
func animationEnded(transitionCompleted: Bool) {
toVC.endAppearanceTransition()
}
interactionControllerForPresentation
这个方法来获得交互控制器,如果返回值为nil的话,才会执行刚刚写的动画,现在我们要使用到这个方法了 UIPercentDrivenInteractiveTransition
,在A中新建一个手势 var gesture:UIScreenEdgePanGestureRecognizer
,设置手势的 action
为 present
到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()
}
}
navigationController
的 delegate
,同时在代理中实现代理方法,也是返回动画管理器,在此就没有多写动画管理器,也是用的刚刚的动画器了。 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
}
}
Github上的ColinEberhardt的 VCTransitionsLibrary 已经封装了一系列的VC自定义切换动画效果,可以在此基础上修改获得更炫酷的转场动画,Come On!