本文是投稿文章,作者:HenryCheng (简书上的链接: http://www.jianshu.com/p/8c29fce5a994)
一、前言
用过格瓦拉电影,或者其他app可能都知道,一种点击按钮用放大效果实现转场的动画现在很流行,效果大致如下
在iOS中,在同一个导航控制器你可以自定义转场动画实现两个viewController之间的过渡。实际上在iOS7之后,通过实现UIViewControllerAnimatedTransitioning或者UIViewControllerContextTransitioning协议,就可以简单的自定义转场动画,比如一个NavigationController的push和pop。还有一点你需要知道的是,我如果有一个矩形,有一个圆,想要在这个矩形上剪出和圆大小相同的面积,那么就要用到CALayer的mask属性,下面用图表达可能会直观些:
laye.mask
现在可能你对mask属性有一点了解了,下面代码的实现中你将会看到具体的实现过程。先做这么多了解,下面开始一步步实现效果。
二、开始实现简单的push效果
新建工程,这里用的是Swift,选中storyboard,然后加上一个导航,如下
添加导航控制器
然后效果如下
去掉导航栏
把右侧的Shows Navigation Bar去掉,因为这个demo里面并不需要导航栏,同时保证Is Instal View Controller是被勾上的(不知道的童鞋可以去掉看一下效果),这里默认的都是勾选上的。然后在新建一个viewController,并设置其继承于ViewController,如下
新建一个viewController
然后在两个VC上分别在同样的位置添加两个完全相同的按钮,位置约束在右上角距离右边和上边分别为20,20的距离,为了区分,将这两个VC设置不同的背景色,如下
按钮的约束位置以及大小
然后右键一直按住第一个按钮拖拽至第二个VC(也就是黄色背景的)点击show
实现第一个 VC 按钮点击方法
这时候两个VC之间就会出现一条线,然后点击线中间,设置identifier为PushSegue,这里设置一个标识符,为后面的跳转做准备,效果如下:
设置identifier
将两个按钮连接成ViewController的同一个属性,名为popBtn,然后将第二个VC的按钮实现一个点击方法(因为我们要pop回来)名为popClick,如下
importUIKit
classViewController:UIViewController{
@IBOutletweak
var
popBtn:UIButton!
overridefuncviewDidLoad(){
super
.viewDidLoad()
//Doanyadditionalsetupafterloadingtheview,typicallyfromanib.
}
@IBActionfuncpopClick(sender:AnyObject){
self.navigationController?.popViewControllerAnimated(
true
)
}
overridefuncdidReceiveMemoryWarning(){
super
.didReceiveMemoryWarning()
//DispoSEOfanyresourcesthatcanberecreated.
}
}
最后,分别在两个VC的中间添加一个imageView,最后的效果图如下
最后效果图
如果到这里你还没错的话,那么运行一下你的工程,运行的效果将会是这样
最后的运行效果图
没错,也就是一个简单的push效果,现在准备工作已经做好了,想要实现放大效果的动画,还要继续往下进行。
三、开始实现放大效果
通过上面的步骤,我们已经做好了准备工作,我们还要知道的一点是,要想自定义导航的push或pop效果,需要实现UINavigationControllerDelegate协议里面的
funcnavigationController(navigationController:UINavigationController,
interactionControllerForAnimationControlleranimationController:UIViewControllerAnimatedTransitioning)->UIViewControllerInteractiveTransitioning?{
return
nil
}
这个协议方法,我们先新建一个继承于NSObject的名为HWNavigationDelegate的一个类,然后引入UINavigationControllerDelegate,实现上面的协议方法,使返回值暂时为nil(从上面代码中可以看出返回值是一个可选值,所以这里可以先用nil,待会再具体实现)。然后你的HWNavigationDelegate里面的代码大致如下
//
//HWNavigationDelegate.swift
//HWAnimationTransition_Swift
//
//CreatedbyHenryChengon16/3/16.
//Copyright?2016年www.igancao.com.Allrightsreserved.
//
importUIKit
classHWNavigationDelegate:NSObject,UINavigationControllerDelegate{
funcnavigationController(navigationController:UINavigationController,animationControllerForOperationoperation:UINavigationControllerOperation,fromViewControllerfromVC:UIViewController,toViewControllertoVC:UIViewController)->UIViewControllerAnimatedTransitioning?{
return
nil;
}
}
现在继续打开storyboard,然后在右下角搜索Object,并将其拖拽至左边Navigation Controller Source里,
添加Object
并在选中Object,在右边将其类改成刚刚创建的HWNavigationDelegate
HWNavigationDelegate.png
最后在左侧,点击UINavigationController,并将其delegate设置为刚才的Object
设置导航的delegate
现在上面HWNavigationDelegate里面导航的协议方法的返回值还是nil,我们需要创建一个实现动画效果的类,并使其返回,这里我们新建一个同样继承于NSObject的名为HWTransitionAnimator的类,并使其实现UIViewControllerAnimatedTransitioning协议,和其中的协议方法,为了便于阅读,这里贴出所有的代码,
//
//HWTransitionAnimator.swift
//HWAnimationTransition_Swift
//
//CreatedbyHenryChengon16/3/16.
//Copyright?2016年www.igancao.com.Allrightsreserved.
//
importUIKit
classHWTransitionAnimator:NSObject,UIViewControllerAnimatedTransitioning{
weak
var
transitionContext:UIViewControllerContextTransitioning?
functransitionDuration(transitionContext:UIViewControllerContextTransitioning?)->NSTimeInterval{
return
0.5
}
funcanimateTransition(transitionContext:UIViewControllerContextTransitioning){
self.transitionContext=transitionContext
letcontainerView=transitionContext.containerView()
letfromVC=transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)as!ViewController
lettoVC=transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)as!ViewController
letbutton=fromVC.popBtn
containerView?.addSubview(toVC.view)
letcircleMaskPathInitial=UIBezierPath(ovalInRect:button.frame)
letextremePoint=CGPoint(x:button.center.x-0,y:button.center.y-CGRectGetHeight(toVC.view.bounds))
letradius=sqrt((extremePoint.x*extremePoint.x)+(extremePoint.y*extremePoint.y))
letcircleMaskPathFinal=UIBezierPath(ovalInRect:CGRectInset(button.frame,-radius,-radius))
letmaskLayer=CAShapeLayer()
maskLayer.path=circleMaskPathFinal.CGPath
toVC.view.layer.mask=maskLayer
letmaskLayerAnimation=CABasicAnimation(keyPath:
"path"
)
maskLayerAnimation.fromValue=circleMaskPathInitial.CGPath
@H_406_502@
maskLayerAnimation.toValue=circleMaskPathFinal.CGPath
maskLayerAnimation.duration=self.transitionDuration(transitionContext)