变更记录

序号 录入时间 录入人 备注
1 2015-03-03 Alfred Jiang -
2 2015-12-22 Alfred Jiang -

方案名称

动画 - 页面跳转 - 自定义模态跳转动画

关键字

动画 \ 跳转 \ UINavigationController \ UINavigationControllerDelegate \ UIViewControllerAnimatedTransitioning

需求场景

  1. 对页面跳转动画有特殊需求时

参考链接

  1. How To Make A View Controller Transition Animation Like in the Ping App

详细内容

#####UINavigationController

  1. 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
//
// NavigationControllerDelegate.swift
// CircleTransition
//
// Created by Rounak Jain on 23/10/14.
// Copyright (c) 2014 Rounak Jain. All rights reserved.
//

import UIKit

class 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
//
// CircleTransitionAnimator.swift
// CircleTransition
//
// Created by Rounak Jain on 23/10/14.
// Copyright (c) 2014 Rounak Jain. All rights reserved.
//

import UIKit

class 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

  1. 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
//
// ModelTransitionDelegate.swift
// GrandJustice
//
// Created by Alfred Jiang on 2/6/15.
// Copyright (c) 2015 FYH. All rights reserved.
//

import UIKit

enum 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
//
// ModelTransitionAnimator.swift
// GrandJustice
//
// Created by Alfred Jiang on 2/6/15.
// Copyright (c) 2015 FYH. All rights reserved.
//

import UIKit

class 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

})

效果图

(无)

备注

(无)