UITabBarController 他的子视图控制器被选中时,切换子视图
UINavigationController push/pop一个子视图时
一个UIViewController 被presented/dismissed时
自定义非交互式过渡动画
1、所要实现自定义过渡动画的ViewController实现对应的delegate,UITabBarViewController对应的是
UITabBarControllerDelegate
,UINavigationController对应的是
UINavigationControllerDelegate
。
2、在动画开始时,delegate会调用对应的动画控制器,也就是实现了
UIViewControllerAnimatedTransitioning
协议的类,这个类可以是任意类型。返回nil,则使用默认动画效果。
3、动画控制器实现对应的方法
transitionDuration:
动画执行时间
animateTransition:
实现自定义动画的地方
其他解释在代码注释中
实现一个简单的例子,将UITabBarController的切换视图方式改成左右移动的方式
Swift版本:
只是个简单的例子,所以建立一个
Tabbled Application
模板的项目
实现AppDelegate即可
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
(self.window!.rootViewController as! UITabBarController).delegate = self
return true
extension AppDelegate : UITabBarControllerDelegate{
func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
extension AppDelegate : UIViewControllerAnimatedTransitioning{
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
let r1start = transitionContext.initialFrameForViewController(vc1)
let r2end = transitionContext.finalFrameForViewController(vc2)
let tab = self.window!.rootViewController as! UITabBarController
let index1 = tab.viewControllers!.indexOf(vc1)
let index2 = tab.viewControllers!.indexOf(vc2)
let dir : CGFloat = index1 < index2 ? 1 : -1
var r1end = r1start
r1end.origin.x -= r1end.width * dir
var r2start = r2end
r2start.origin.x += r2start.width * dir
view2.frame = r2start
containerView.addSubview(view2)
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
UIView.animateWithDuration(0.4, animations: {
view1.frame = r1end
view2.frame = r2end
}) { (_) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
为了让代码显得整洁一些,并且可以是该动画可以在多出更方便的使用,我们将其独立出来。
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
(self.window!.rootViewController as! UITabBarController).delegate = self
return true
extension AppDelegate : UITabBarControllerDelegate{
func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
let animation = CustomTransitionAimation()
animation.tab = (self.window!.rootViewController as! UITabBarController)
return animation
动画类的实现
import UIKit
class CustomTransitionAimation: NSObject,UIViewControllerAnimatedTransitioning {
var tab : UITabBarController?
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
let r1start = transitionContext.initialFrameForViewController(vc1)
let r2end = transitionContext.finalFrameForViewController(vc2)
let tab = self.tab!
let index1 = tab.viewControllers!.indexOf(vc1)
let index2 = tab.viewControllers!.indexOf(vc2)
let dir : CGFloat = index1 < index2 ? 1 : -1
var r1end = r1start
r1end.origin.x -= r1end.width * dir
var r2start = r2end
r2start.origin.x += r2start.width * dir
view2.frame = r2start
containerView.addSubview(view2)
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
UIView.animateWithDuration(0.4, animations: {
view1.frame = r1end
view2.frame = r2end
}) { (_) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
Objective-C版本
#import "AppDelegate.h"
#import "CusomTransitionAnimation.h"
@interface AppDelegate ()<UITabBarControllerDelegate>
@property(nonatomic,strong) UITabBarController *tabBarController;
@property(nonatomic,strong) CusomTransitionAnimation *customAnimation;
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.tabBarController = (UITabBarController *)self.window.rootViewController;
self.tabBarController.delegate = self;
return YES;
- (id <UIViewControllerAnimatedTransitioning> )tabBarController:(UITabBarController *)tabBarController animationControllerForTransitionFromViewController:(UIViewController *)fromVC toViewController:(UIViewController *)toVC
CusomTransitionAnimation *animation = [[CusomTransitionAnimation alloc]init];
animation.tabBarController = self.tabBarController;
return animation;
- (CusomTransitionAnimation *)customAnimation
if (!_customAnimation) {
_customAnimation = [[CusomTransitionAnimation alloc]init];
_customAnimation.tabBarController = self.tabBarController;
return _customAnimation;
@interface CusomTransitionAnimation : NSObject<UIViewControllerAnimatedTransitioning>
@property(nonatomic,strong) UITabBarController *tabBarController
@implementation CusomTransitionAnimation
- (NSTimeInterval)transitionDuration:(nullable id <UIViewControllerContextTransitioning>)transitionContext
return 0.4
- (void)animateTransition:(id <UIViewControllerContextTransitioning>)transitionContext
UIView *containerView = [transitionContext containerView]
UIViewController *vc1 = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]
UIViewController *vc2 = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]
UIView *view1 = [transitionContext viewForKey:UITransitionContextFromViewKey]
UIView *view2 = [transitionContext viewForKey:UITransitionContextToViewKey]
CGRect vc1start = [transitionContext initialFrameForViewController:vc1]
CGRect vc2end = [transitionContext finalFrameForViewController:vc2]
NSUInteger index1 = [self.tabBarController.viewControllers indexOfObject:vc1]
NSUInteger index2 = [self.tabBarController.viewControllers indexOfObject:vc2]
int dir = index1 < index2 ? 1 : -1
CGRect vc1end = vc1start
vc1end.origin.x -= vc1end.size.width * dir
CGRect vc2start = vc2end
vc2start.origin.x += vc2start.size.width * dir
view2.frame = vc2start
[containerView addSubview:view2]
[[UIApplication sharedApplication]beginIgnoringInteractionEvents]
[UIView animateWithDuration:0.4 animations:^{
view1.frame = vc1end;
view2.frame = vc2end;
} completion:^(BOOL finished) {
[transitionContext completeTransition:(!transitionContext.transitionWasCancelled)]
[[UIApplication sharedApplication]endIgnoringInteractionEvents]
- (void)animationEnded:(BOOL)transitionCompleted
if (transitionCompleted) {
NSLog(@"Completed")
}else{
NSLog(@"No Completed")
UINavigationController实现是类似的,只不过调用动画类的方式不同
viewController实现
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController!.delegate = self
@IBAction func pushAction(sender: AnyObject) {
let second = SecondViewController()
self.navigationController!.pushViewController(second, animated: true)
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
extension ViewController : UINavigationControllerDelegate{
func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
if operation == .Push {
return CustomPushTransitionAimation()
else if operation == .Pop{
return CustomPopTransitionAimation()
return nil
Push动画
import UIKit
class CustomPushTransitionAimation: NSObject,UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()!
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
view2.transform = CGAffineTransformMakeScale(0.1, 0.1)
containerView.addSubview(view2)
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
UIView.animateWithDuration(0.4, animations: {
view2.transform = CGAffineTransformIdentity
}) { (_) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
Pop动画
import UIKit
class CustomPushTransitionAimation: NSObject,UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()!
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
view2.transform = CGAffineTransformMakeScale(0.1, 0.1)
containerView.addSubview(view2)
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
UIView.animateWithDuration(0.4, animations: {
view2.transform = CGAffineTransformIdentity
}) { (_) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
可交互的自定义过渡动画
有两种实现方式
使用百分比驱动
使用这种方式比较简单,还是按照上述的方法先实现不可交互的自定义动画。接下来实现回调方法
func tabBarController(tabBarController: UITabBarController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
主要是UIPercentDrivenInteractiveTransition
的使用,实质是frozen animation。
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var rightEdgr : UIScreenEdgePanGestureRecognizer!
var leftEdgr : UIScreenEdgePanGestureRecognizer!
var inter : UIPercentDrivenInteractiveTransition!
var interacting = false
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let tab = (self.window!.rootViewController as! UITabBarController)
tab.delegate = self
let sep = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(pan))
sep.edges = .Right
sep.delegate = self
tab.view.addGestureRecognizer(sep)
self.rightEdgr = sep
let sep2 = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(pan))
sep2.edges = .Left
sep2.delegate = self
tab.view.addGestureRecognizer(sep2)
self.leftEdgr = sep2
return true
extension AppDelegate : UITabBarControllerDelegate{
func tabBarController(tabBarController: UITabBarController, animationControllerForTransitionFromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
func tabBarController(tabBarController: UITabBarController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
if self.interacting {
return self.inter
return nil
extension AppDelegate : UIGestureRecognizerDelegate{
func gestureRecognizerShouldBegin(gestureRecognizer: UIGestureRecognizer) -> Bool {
let tab = self.window!.rootViewController as! UITabBarController
var result = false
if gestureRecognizer == self.rightEdgr {
result = (tab.selectedIndex < tab.viewControllers!.count - 1)
else{
result = tab.selectedIndex > 0
return result
func pan(g : UIScreenEdgePanGestureRecognizer) -> Void {
let v = g.view!
let tab = self.window!.rootViewController as! UITabBarController
let delta = g.translationInView(v)
let percent = fabs(delta.x/v.bounds.width)
switch g.state {
case .Began:
self.inter = UIPercentDrivenInteractiveTransition()
self.interacting = true
if g == self.rightEdgr {
tab.selectedIndex = tab.selectedIndex + 1
}else{
tab.selectedIndex = tab.selectedIndex - 1
print("selectedIndex = \(tab.selectedIndex)")
case .Changed:
self.inter.updateInteractiveTransition(percent)
case .Ended:
if percent > 0.5 {
self.inter.finishInteractiveTransition()
}else{
self.inter.cancelInteractiveTransition()
self.interacting = false
case .Cancelled:
self.inter.cancelInteractiveTransition()
self.interacting = false
default : break
extension AppDelegate : UIViewControllerAnimatedTransitioning{
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let vc1 = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!
let vc2 = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let containerView = transitionContext.containerView()!
let view1 = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let view2 = transitionContext.viewForKey(UITransitionContextToViewKey)!
let r1start = transitionContext.initialFrameForViewController(vc1)
let r2end = transitionContext.finalFrameForViewController(vc2)
let tab = self.window!.rootViewController as! UITabBarController
let index1 = tab.viewControllers!.indexOf(vc1)
let index2 = tab.viewControllers!.indexOf(vc2)
let dir : CGFloat = index1 < index2 ? 1 : -1
var r1end = r1start
r1end.origin.x -= r1end.width * dir
var r2start = r2end
r2start.origin.x += r2start.width * dir
view2.frame = r2start
containerView.addSubview(view2)
//避免其他手势事件的干扰
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
//执行具体动画,不同效果的代码不同处就是在这了
UIView.animateWithDuration(0.4, animations: {
view1.frame = r1end
view2.frame = r2end
}) { (_) in
//防止取消状态
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
UIApplication.sharedApplication().endIgnoringInteractionEvents()
let opts : UIViewAnimationOptions = self.interacting ? .CurveLinear : []
if !self.interacting {
UIApplication.sharedApplication().beginIgnoringInteractionEvents()
UIView.animateWithDuration(0.4, delay: 0, options: opts, animations: {
view1.frame = r1end
view2.frame = r2end
}) { (_) in
let canceld = transitionContext.transitionWasCancelled()
transitionContext.completeTransition(!canceld)
if UIApplication.sharedApplication().isIgnoringInteractionEvents(){
UIApplication.sharedApplication().endIgnoringInteractionEvents()
func animationEnded(transitionCompleted: Bool) {
if transitionCompleted {
print("Completed")
}else{
print("NO Completed")
let tab = self.window!.rootViewController as! UITabBarController
print(tab.selectedIndex)
不使用百分比驱动
这种写法虽然看起来会稍微复杂一些,但是他的可定制程度更高,因为我们可以直接对fromView 和 toView等进行直接操作。
不在需要具体实现animateTransition
方法,但是他还是要存在的,因为他的协议必须实现的方法。
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
将其中的代码放到实现了协议UIViewControllerInteractiveTransitioning
的类中实现。放到方法
func startInteractiveTransition(transitionContext: UIViewControllerContextTransitioning){
中就可以了。
这次使用OC实现,都熟悉下:
@interface AppDelegate ()<UIViewControllerAnimatedTransitioning,UIViewControllerInteractiveTransitioning,UITabBarControllerDelegate,UIGestureRecognizerDelegate>
@property(nonatomic,strong) id<UIViewControllerContextTransitioning>transitionContext
@property(nonatomic,strong) UIScreenEdgePanGestureRecognizer *leftEdgr
@property(nonatomic,strong) UIScreenEdgePanGestureRecognizer *rightEdgr
@property(nonatomic,assign) BOOL interacting
@property(nonatomic,assign) CGRect r1end
@property(nonatomic,assign) CGRect r2start
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
UITabBarController *tab = (UITabBarController *)self.window.rootViewController
tab.delegate = self
self.leftEdgr = ({
UIScreenEdgePanGestureRecognizer *pan = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)]
pan.edges = UIRectEdgeLeft
pan.delegate = self
[tab.view addGestureRecognizer:pan]
pan
self.rightEdgr = ({
UIScreenEdgePanGestureRecognizer *pan = [[UIScreenEdgePanGestureRecognizer alloc]initWithTarget:self action:@selector(pan:)]
pan.edges = UIRectEdgeRight
pan.delegate = self
[tab.view addGestureRecognizer:pan]
pan
self.r1end = CGRectZero
self.r2start = CGRectZero
return YES
- (void)pan:(UIScreenEdgePanGestureRecognizer *)ges
UIView *gesView = ges.view
UITabBarController *tab = (UITabBarController *)self.window.rootViewController
CGPoint delta = [ges translationInView:gesView]
CGFloat percent = delta.x/gesView.bounds.size.width
UIViewController *fromVC,*toVC
UIView *fromView,*toView
CGRect fromRectStart,toRectEnd
if (self.transitionContext) {
fromVC = [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]
toVC = [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]
fromView = [self.transitionContext viewForKey:UITransitionContextFromViewKey]
toView = [self.transitionContext viewForKey:UITransitionContextToViewKey]
fromRectStart = [self.transitionContext initialFrameForViewController:fromVC]
toRectEnd = [self.transitionContext finalFrameForViewController:toVC]
switch (ges.state) {
case UIGestureRecognizerStateBegan: {
self.interacting = YES
if (ges == self.leftEdgr) {
tab.selectedIndex = tab.selectedIndex - 1
}else{
tab.selectedIndex = tab.selectedIndex + 1
break
case UIGestureRecognizerStateChanged:{
//可以在这里对fromView 和 toView 进行直接操作,所以,可定制程度高
fromRectStart.origin.x += (self.r1end.origin.x - fromRectStart.origin.x) * percent
fromView.frame = fromRectStart
NSLog(@"fromRect : %@",NSStringFromCGRect(fromRectStart))
CGRect toRectStart = self.r2start
toRectStart.origin.x += (toRectStart.origin.x - self.r2start.origin.x) * percent
toView.frame = toRectStart
NSLog(@"toRect : %@",NSStringFromCGRect(toRectStart))
//在UITabBarController的切换中可能看不出来这句话有什么用处,但是在UINavigationController中这个就很有用了,可以自己处理NavigationBar的效果等。
[self.transitionContext updateInteractiveTransition:percent]
break
case UIGestureRecognizerStateEnded: {
if (percent > 0.5) {
//达到要求,设置成最终状态
[UIView animateWithDuration:0.2 animations:^{
fromView.frame = self.r1end;
toView.frame = toRectEnd;
} completion:^(BOOL finished) {
[self.transitionContext finishInteractiveTransition]
[self.transitionContext completeTransition:YES]
else{
//恢复原状
[UIView animateWithDuration:0.2 animations:^{
fromView.frame = fromRectStart;
toView.frame = self.r2start;
} completion:^(BOOL finished) {
[self.transitionContext cancelInteractiveTransition]
[self.transitionContext completeTransition:NO]
self.interacting = NO
self.transitionContext = nil
break
case UIGestureRecognizerStateCancelled: {
//恢复原状
fromView.frame = fromRectStart
toView.frame = self.r2start
[self.transitionContext finishInteractiveTransition]
[self.transitionContext completeTransition:NO]
self.interacting = NO
self.transitionContext = nil
break
default:
break
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
UITabBarController *tab = (UITabBarController *)self.window.rootViewController
BOOL result = NO
if (gestureRecognizer == self.leftEdgr) {
result = tab.selectedIndex > 0
}else{
result = tab.selectedIndex < tab.viewControllers.count - 1
return result
- (nullable id <UIViewControllerInteractiveTransitioning>)tabBarController:(UITabBarController *)tabBarController
interactionControllerForAnimationController: (id <UIViewControllerAnimatedTransitioning>)animationController
return self.interacting ? self : nil
- (nullable id <UIViewControllerAnimatedTransitioning>)tabBarController:(UITabBarController *)tabBarController
animationControllerForTransitionFromViewController:(UIViewController *)fromVC
toViewController:(UIViewController *)toVC
return self.interacting ? self : nil
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
return 0.4
//这个要实现,但是要保持为空
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
//这个其实就是要实现 animateTransition: 原来实现的功能
- (void)startInteractiveTransition:(id<UIViewControllerContextTransitioning>)transitionContext
self.transitionContext = transitionContext
//即最终要达到的状态
UIView *containerView = [transitionContext containerView]
UIViewController *fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey]
UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey]
// UIView *fromView = [transitionContext viewForKey:UITransitionContextFromViewKey]
UIView *toView = [transitionContext viewForKey:UITransitionContextToViewKey]
CGRect fromRectStart = [transitionContext initialFrameForViewController:fromVC]
CGRect toRectEnd = [transitionContext finalFrameForViewController:toVC]
UITabBarController *tab = (UITabBarController *)self.window.rootViewController
NSUInteger fromIndex = [tab.viewControllers indexOfObject:fromVC]
NSUInteger toIndex = [tab.viewControllers indexOfObject:toVC]
int dir = fromIndex < toIndex ? 1 : -1
CGRect fromRectEnd = fromRectStart
fromRectEnd.origin.x -= fromRectEnd.size.width * dir
CGRect toRectStart = toRectEnd
toRectStart.origin.x += toRectStart.size.width * dir
toView.frame = toRectStart
[containerView addSubview:toView]
self.r1end = fromRectEnd
self.r2start = toRectStart
自定义Presented ViewController的过渡动画
实现主要要两种方式
不使用presentation controller
使用presentation controller
不使用presentation controller
需要注意的地方是
协议transitioningDelegate声明的位置
区分present 和 dismiss
只有当modalPresentationStyle *不是 .FullScreen *的时候,才能使用如下方式判定是present还是dismiss
import UIKit
class ViewController2: UIViewController {
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.transitioningDelegate = self
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.transitioningDelegate = self
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blueColor()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.presentingViewController!.dismissViewControllerAnimated(true, completion:
nil)
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
extension ViewController2 : UIViewControllerTransitioningDelegate{
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return self
extension ViewController2 : UIViewControllerAnimatedTransitioning{
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.4
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView()!
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)
if fromView != nil {
UIView.animateWithDuration(0.4, animations: {
fromView!.transform = CGAffineTransformMakeScale(0.1, 0.1)
fromView!.alpha = 0
}, completion: { (_) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
else if toView != nil{
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!
let toRectEnd = transitionContext.finalFrameForViewController(toVC)
toView!.frame = CGRectInset(toRectEnd, 40, 40)
toView!.transform = CGAffineTransformMakeScale(0.1, 0.1)
toView!.alpha = 0
containerView.addSubview(toView!)
UIView.animateWithDuration(0.4, animations: {
toView!.transform = CGAffineTransformIdentity
toView!.alpha = 1.0
}) { (_) in
transitionContext.completeTransition(!transitionContext.transitionWasCancelled())
在.FullScreen 类型的presented中,会取得presenting View,并将其添加到containerView中,其他类型的则是,containerView是在presenting view之前的,不能给containerView添加动画,并且containerView不会移除。
使用Presentation Controller
在此之前,需要理解的一些东西
Animation Controller 用来负责动画效果,也就是presented View 如何移动到最终目标位置
Presentation Controller 确定presented View的最终位置,还有向containerView上添加一些视图,例如半透明效果什么的。
举个例子来说明,如果只是实现了UIPresentationController 而没有做上面的那些工作,会使用默认动画弹出。
接下来说说实现:
在实现上面效果的基础上,需要在做几点工作
在设置presented viewController的 transitioningDelegate属性的同时,还需要设置其的modalPresentationStyle为.Custom,这个是必须的。(注意代码位置)
self.transitioningDelegate = self
if UIApplication.sharedApplication().keyWindow!.traitCollection.userInterfaceIdiom == .Phone {
self.modalPresentationStyle = .Custom
在实现另一个方法
@available(iOS 8.0, *)
optional public func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController?
接下来便是在 UIPresentationController的子类中重载他的方法来达到我们所要的效果
方法的说明:
public func frameOfPresentedViewInContainerView() -> CGRect
返回presented view的最终位置
使用下面的方法来给containerView添加或移除一些额外的视图
presentationTransitionWillBegin
presentationTransitionDidEnd
dismissalTransitionWillBegin
dismissalTransitionDidEnd
使用下面方法来个添加的额外视图进行布局
containerViewWillLayoutSubviews
containerViewDidLayoutSubviews
shouldPresentInFullscreen
默认返回的是true,如果返回false,会将presentation改变为 .CurrentContext
主要就这几个,完结。
Transition Coordinator的使用
主要注意几个方法的使用就可以了
animateAlongsideTransition:completion:
可以用来给已经在视图上的子视图添加动画效果
- (void)viewWillAppear:(BOOL)animated
[super viewWillAppear:animated];
if (self.transitionCoordinator != nil) {
[self.transitionCoordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
NSLog(@"Transition Animation");
self.animationView.center = self.view.center;
} completion:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
NSLog(@"Transition completion");
else{
NSLog(@"Transition is NULL");
notifyWhenInteractionEndsUsingBlock:
监控视图返回时手势动作是否成功。比如在UINavigationController中左侧滑动返回时,需要在上一个ViewController中实现。
- (void)viewWillAppear:(BOOL)animated
[super viewWillAppear:animated];
if (self.transitionCoordinator != nil) {
[self.transitionCoordinator notifyWhenInteractionEndsUsingBlock:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
if ([context isCancelled]) {
NSLog(@"1 Interaction Cancelled");
return ;
NSLog(@"1 Interaction Ends");
else{
NSLog(@"1 Transition is NULL");
}复制代码