写在前面
iOS在modal 或push等操作时有默认的转场动画,但有时候我们又需要特定的转场动画效果,从iOS7开始,苹果就提供了自定义转场的API,模态推送present和dismiss、导航控制器push和pop、标签控制器的控制器切换都可以自定义转场。
自定义转场动画的实现步骤如下:
1、遵循<UIViewControllerAnimatedTransitioning>
协议的动画过渡管理对象,两个必须实现的方法:
public func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval // This method can only be a nop if the transition is interactive and not a percentDriven interactive transition. public func animateTransition(transitionContext: UIViewControllerContextTransitioning) // This is a convenience and if implemented will be invoked by the system when the transition context's completeTransition: method is invoked. optional public func animationEnded(transitionCompleted: Bool)
2、继承于UIPercentDrivenInteractiveTransition
的手势过渡管理对象,动画的过程是通过百分比控制的。如果不需要手势控制,这一步可不实现。
UIViewControllerInteractiveTransitioning { // This is the non-interactive duration that was returned when the // animators transitionDuration: method was called when the transition started. public var duration: CGFloat { get } // The last percentComplete value specified by updateInteractiveTransition: public var percentComplete: CGFloat { get } // completionSpeed defaults to 1.0 which corresponds to a completion duration of // (1 - percentComplete)*duration. It must be greater than 0.0. The actual // completion is inversely proportional to the completionSpeed. This can be set // before cancelInteractiveTransition or finishInteractiveTransition is called // in order to speed up or slow down the non interactive part of the // transition. public var completionSpeed: CGFloat // When the interactive part of the transition has completed, this property can // be set to indicate a different animation curve. It defaults to UIViewAnimationCurveEaseInOut. // Note that during the interactive portion of the animation the timing curve is linear. public var completionCurve: UIViewAnimationCurve // These methods should be called by the gesture recognizer or some other logic // to drive the interaction. This style of interaction controller should only be // used with an animator that implements a CA style transition in the animator's // animateTransition: method. If this type of interaction controller is // specified, the animateTransition: method must ensure to call the // UIViewControllerTransitionParameters completeTransition: method. The other // interactive methods on UIViewControllerContextTransitioning should NOT be // called. public func updateInteractiveTransition(percentComplete: CGFloat) public func cancelInteractiveTransition() public func finishInteractiveTransition()
3、成为相应的代理,实现UIViewControllerAnimatedTransitioning的代理方法,返回我们前两步自定义的对象。
模态推送实现的代理方法
@available(iOS 2.0, *) optional public func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? @available(iOS 2.0, *) optional public func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? optional public func interactionControllerForPresentation(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? optional public func interactionControllerForDismissal(animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? @available(iOS 8.0, *) optional public func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController?
转场动画Demo
下面直接看demo,封装自定义的转场动画实现图片在两个控制器间放大过渡的动画效果,面向协议进行开发,使用时将转场动画代理设置为SZAnimator。
import UIKit protocol SZAnimatorPresentDelegate: NSObjectProtocol { // 负责提供弹出动画的视图 func presentView() -> UIView func presentFrameRect() -> CGRect func presentToRect() -> CGRect } protocol SZAnimatorDimissDelegate: NSObjectProtocol { // 负责提供dismiss动画的视图 func dismissView() -> UIView func dismissFrameRect() -> CGRect func dismissToRect() -> CGRect } class SZAnimator: NSObject { var isPresent: Bool = true weak var presentDelegate: SZAnimatorPresentDelegate? weak var dismissDelegate: SZAnimatorDimissDelegate? } extension SZAnimator: UIViewControllerTransitioningDelegate { // 指定弹出时,处理动画的对象 func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresent = true return self } // 指定弹下去时候, 处理动画的对象 func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { isPresent = false return self } } // 消失动画 // 弹出动画 extension SZAnimator: UIViewControllerAnimatedTransitioning { // 返回动画的时间 func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval { return 2.0 } // 在这里, 实现真正的弹出, 或者消失的动画 // transitionContext func animateTransition(transitionContext: UIViewControllerContextTransitioning) { isPresent ? present(transitionContext) : dismiss(transitionContext) } func dismiss(transitionContext: UIViewControllerContextTransitioning) { // 自定义动画 // 面向协议进行开发 // 1. 拿什么界面做动画 // 2. fromRect // 3. toRect let animationView = dismissDelegate!.dismissView() transitionContext.containerView()?.addSubview(animationView) // 初始的frame animationView.frame = dismissDelegate!.dismissFrameRect() // 最终需要展示的视图 if #available(iOS 8.0, *) { let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey) // 动画 UIView.animateWithDuration(transitionDuration(transitionContext), animations: { // toView?.alpha = 1 animationView.frame = self.dismissDelegate!.dismissToRect() fromView?.alpha = 0 }) { (flag: Bool) in animationView.removeFromSuperview() transitionContext.completeTransition(true) } } } func present(transitionContext: UIViewControllerContextTransitioning) { // 自定义动画 // 面向协议进行开发 // 1. 拿什么界面做动画 // 2. fromRect // 3. toRect let animationView = presentDelegate!.presentView() transitionContext.containerView()?.addSubview(animationView) // 初始的frame animationView.frame = presentDelegate!.presentFrameRect() // 最终需要展示的视图 if #available(iOS 8.0, *) { let toView = transitionContext.viewForKey(UITransitionContextToViewKey) toView?.frame = UIScreen.mainScreen().bounds transitionContext.containerView()?.addSubview(toView!) toView?.alpha = 0 // 动画 UIView.animateWithDuration(2.0, animations: { toView?.alpha = 1 animationView.frame = self.presentDelegate!.presentToRect() }) { (flag: Bool) in animationView.removeFromSuperview() transitionContext.completeTransition(true) } } } }