变更记录
方案名称 动画 - 页面跳转 - 自定义模态跳转动画
关键字 动画 \ 跳转 \ UINavigationController \ UINavigationControllerDelegate \ UIViewControllerAnimatedTransitioning
需求场景
对页面跳转动画有特殊需求时
参考链接
How To Make A View Controller Transition Animation Like in the Ping App
详细内容 #####UINavigationController
Swift 解决方案
定义 NavigationControllerDelegate.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import UIKitclass NavigationControllerDelegate : NSObject , UINavigationControllerDelegate { var startPoint : CGPoint ? weak var navigationController: UINavigationController ? var interactionController: UIPercentDrivenInteractiveTransition ? override func awakeFromNib () { super .awakeFromNib() var panGesture = UIPanGestureRecognizer (target: self , action: Selector ("panned:" )) self .navigationController! .view.addGestureRecognizer(panGesture) } func panned (gestureRecognizer : UIPanGestureRecognizer ) { switch gestureRecognizer.state { case .Began : startPoint = gestureRecognizer.locationInView(self .navigationController? .view) if startPoint! .x < 20 { self .interactionController = UIPercentDrivenInteractiveTransition () if self .navigationController? .viewControllers.count > 1 { GJViewTools .CLICK_POINT = gestureRecognizer.locationInView(self .navigationController? .view) GJViewTools .DRAG_POINT = true self .navigationController? .popViewControllerAnimated(true ) } } case .Changed : if startPoint! .x < 20 { var translation = gestureRecognizer.translationInView(self .navigationController! .view) var completionProgress = translation.x/ CGRectGetWidth (self .navigationController! .view.bounds) self .interactionController? .updateInteractiveTransition(completionProgress) } case .Ended : if startPoint! .x < 20 { if (gestureRecognizer.velocityInView(self .navigationController! .view).x > 0 ) { self .interactionController? .finishInteractiveTransition() } else { self .interactionController? .cancelInteractiveTransition() } self .interactionController = nil } default : self .interactionController? .cancelInteractiveTransition() self .interactionController = nil } } func navigationController (navigationController : UINavigationController , animationControllerForOperation operation : UINavigationControllerOperation , fromViewController fromVC : UIViewController , toViewController toVC : UIViewController ) -> UIViewControllerAnimatedTransitioning ? { var cirAnimator : CircleTransitionAnimator = CircleTransitionAnimator () cirAnimator.navigationOperation = operation return cirAnimator } func navigationController (navigationController : UINavigationController , interactionControllerForAnimationController animationController : UIViewControllerAnimatedTransitioning ) -> UIViewControllerInteractiveTransitioning ? { return self .interactionController } }
CircleTransitionAnimator.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 import UIKitclass CircleTransitionAnimator : NSObject , UIViewControllerAnimatedTransitioning { weak var transitionContext: UIViewControllerContextTransitioning ? var navigationOperation: UINavigationControllerOperation ? func transitionDuration (transitionContext : UIViewControllerContextTransitioning ) -> NSTimeInterval { return GJViewTools .DURATION_TIME ; } func animateTransition (transitionContext : UIViewControllerContextTransitioning ) { self .transitionContext = transitionContext var containerView = transitionContext.containerView() var fromViewController : UIViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey )! var toViewController : UIViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey )! var button = UIButton (frame: CGRectMake (0 , 0 , 10 , 10 )) button.center = GJViewTools .CLICK_POINT if navigationOperation == UINavigationControllerOperation .Pop && ! GJViewTools .DRAG_POINT { var snapshotImageView: UIView = fromViewController.view.snapshotViewAfterScreenUpdates(false ) toViewController.view.addSubview(snapshotImageView) containerView.addSubview(toViewController.view) var circleMaskPathInitial = UIBezierPath (ovalInRect: button.frame) var radius = sqrt((SCREEN_HEIGH * SCREEN_HEIGH ) + (SCREEN_WIDTH * SCREEN_WIDTH )) var circleMaskPathFinal = UIBezierPath (ovalInRect: CGRectInset (button.frame, - radius, - radius)) var maskLayer = CAShapeLayer () maskLayer.path = circleMaskPathFinal.CGPath snapshotImageView.layer.mask = maskLayer var maskLayerAnimation = CABasicAnimation (keyPath: "path" ) maskLayerAnimation.fromValue = circleMaskPathFinal.CGPath maskLayerAnimation.toValue = circleMaskPathInitial.CGPath maskLayerAnimation.duration = self .transitionDuration(transitionContext) maskLayerAnimation.delegate = self maskLayer.addAnimation(maskLayerAnimation, forKey: "path" ) self .delay(self .transitionDuration(transitionContext) - 0.1 , closure: { () -> () in snapshotImageView.removeFromSuperview() }) } else { containerView.addSubview(toViewController.view) var circleMaskPathInitial = UIBezierPath (ovalInRect: button.frame) var radius = sqrt((SCREEN_HEIGH * SCREEN_HEIGH ) + (SCREEN_WIDTH * SCREEN_WIDTH )) var circleMaskPathFinal = UIBezierPath (ovalInRect: CGRectInset (button.frame, - radius, - radius)) var maskLayer = CAShapeLayer () maskLayer.path = circleMaskPathFinal.CGPath toViewController.view.layer.mask = maskLayer var maskLayerAnimation = CABasicAnimation (keyPath: "path" ) maskLayerAnimation.fromValue = circleMaskPathInitial.CGPath maskLayerAnimation.toValue = circleMaskPathFinal.CGPath maskLayerAnimation.duration = self .transitionDuration(transitionContext) maskLayerAnimation.delegate = self maskLayer.addAnimation(maskLayerAnimation, forKey: "path" ) } } override func animationDidStop (anim : CAAnimation !, finished flag : Bool ) { self .transitionContext? .completeTransition(! self .transitionContext! .transitionWasCancelled()) self .transitionContext? .viewControllerForKey(UITransitionContextFromViewControllerKey )? .view.layer.mask = nil } }
使用
1 2 3 4 5 var navDelegate : NavigationControllerDelegate = NavigationControllerDelegate ()navDelegate.navigationController = frontNav frontNav.delegate = navDelegate navDelegate.awakeFromNib()
#####ModelTransition
Swift 解决方案
定义 ModelTransitionDelegate.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 import UIKitenum ModalPresentingType { case Present , Dismiss } class ModelTransitionDelegate : NSObject ,UIViewControllerTransitioningDelegate { func animationControllerForPresentedController (presented : UIViewController , presentingController presenting : UIViewController , sourceController source : UIViewController ) -> UIViewControllerAnimatedTransitioning ? { var modelAnimator : ModelTransitionAnimator = ModelTransitionAnimator () modelAnimator.modalPresentingType = ModalPresentingType .Present return modelAnimator } func animationControllerForDismissedController (dismissed : UIViewController ) -> UIViewControllerAnimatedTransitioning ? { var modelAnimator : ModelTransitionAnimator = ModelTransitionAnimator () modelAnimator.modalPresentingType = ModalPresentingType .Dismiss return modelAnimator } }
ModelTransitionAnimator.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 import UIKitclass ModelTransitionAnimator : NSObject ,UIViewControllerAnimatedTransitioning { var modalPresentingType: ModalPresentingType ? weak var transitionContext: UIViewControllerContextTransitioning ? func transitionDuration (transitionContext : UIViewControllerContextTransitioning ) -> NSTimeInterval { return 0.6 } func animateTransition (transitionContext : UIViewControllerContextTransitioning ) { self .transitionContext = transitionContext var containerView = transitionContext.containerView() var fromViewController : UIViewController = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey )! var toViewController : UIViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey )! var button = UIButton (frame: CGRectMake (0 , 0 , 10 , 10 )) button.center = GJViewTools .CLICK_POINT if modalPresentingType == ModalPresentingType .Dismiss { var snapshotImageView: UIView = fromViewController.view.snapshotViewAfterScreenUpdates(false ) toViewController.view.addSubview(snapshotImageView) containerView.addSubview(toViewController.view) var circleMaskPathInitial = UIBezierPath (ovalInRect: button.frame) var radius = sqrt((SCREEN_HEIGH * SCREEN_HEIGH ) + (SCREEN_WIDTH * SCREEN_WIDTH )) var circleMaskPathFinal = UIBezierPath (ovalInRect: CGRectInset (button.frame, - radius, - radius)) var maskLayer = CAShapeLayer () maskLayer.path = circleMaskPathFinal.CGPath snapshotImageView.layer.mask = maskLayer var maskLayerAnimation = CABasicAnimation (keyPath: "path" ) maskLayerAnimation.fromValue = circleMaskPathFinal.CGPath maskLayerAnimation.toValue = circleMaskPathInitial.CGPath maskLayerAnimation.duration = self .transitionDuration(transitionContext) maskLayerAnimation.delegate = self maskLayer.addAnimation(maskLayerAnimation, forKey: "path" ) self .delay(self .transitionDuration(transitionContext) - 0.1 , closure: { () -> () in snapshotImageView.removeFromSuperview() }) } else { containerView.addSubview(toViewController.view) var circleMaskPathInitial = UIBezierPath (ovalInRect: button.frame) var radius = sqrt((SCREEN_HEIGH * SCREEN_HEIGH ) + (SCREEN_WIDTH * SCREEN_WIDTH )) var circleMaskPathFinal = UIBezierPath (ovalInRect: CGRectInset (button.frame, - radius, - radius)) var maskLayer = CAShapeLayer () maskLayer.path = circleMaskPathFinal.CGPath toViewController.view.layer.mask = maskLayer var maskLayerAnimation = CABasicAnimation (keyPath: "path" ) maskLayerAnimation.fromValue = circleMaskPathInitial.CGPath maskLayerAnimation.toValue = circleMaskPathFinal.CGPath maskLayerAnimation.duration = self .transitionDuration(transitionContext) maskLayerAnimation.delegate = self maskLayer.addAnimation(maskLayerAnimation, forKey: "path" ) } } override func animationDidStop (anim : CAAnimation !, finished flag : Bool ) { self .transitionContext? .completeTransition(! self .transitionContext! .transitionWasCancelled()) self .transitionContext? .viewControllerForKey(UITransitionContextFromViewControllerKey )? .view.layer.mask = nil } }
使用
1 2 3 4 5 6 7 8 var transDelegate : ModelTransitionDelegate = ModelTransitionDelegate ()var helpNav = UINavigationController (rootViewController: tipsVC)helpNav.transitioningDelegate = self .transDelegate self .presentViewController(helpNav, animated: true , completion: { () -> Void in })
效果图 (无)
备注 (无)