神刀安全网

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

一、前言

用过格瓦拉电影,或者其他 app 可能都知道,一种点击按钮用放大效果实现转场的动画现在很流行,效果大致如下

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
tap_oc.gif

iOS 中,在同一个导航控制器你可以自定义转场动画实现两个 viewVontroller 之间的过渡。实际上在 iOS7 之后,通过实现 UIViewControllerAnimatedTransitioning 或者 UIViewControllerContextTransitioning 协议,就可以简单的自定义转场动画,比如一个 NavigationControllerpushpop

还有一点你需要知道的是,我如果有一个矩形,有一个圆,想要在这个矩形上剪出和圆大小相同的面积,那么就要用到 CALayermask 属性,下面用图表达可能会直观些:

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
laye.mask.png

现在可能你对 mask 属性有一点了解了,下面代码的实现中你将会看到具体的实现过程。先做这么多了解,下面开始一步步实现效果。

二、开始实现简单的 push 效果

新建工程,这里用的是 Swift ,选中 storyboard ,然后加上一个导航,如下

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

添加导航控制器.png

然后效果如下

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
去掉导航栏.png

把右侧的 Shows Navigation Bar 去掉,因为这个 demo 里面并不需要导航栏,同时保证 Is Instal View Controller 是被勾上的(不知道的童鞋可以去掉看一下效果),这里默认的都是勾选上的。

然后在新建一个 viewController ,并设置其继承与 ViewController ,如下

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

新建一个viewController

然后在两个 VC 上分别在同样的位置添加两个完全相同的按钮,位置约束在右上角距离右边和上边分别为 2020 的距离,为了区分,将这两个 VC 设置不同的背景色,如下

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

按钮的约束位置以及大小.png

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

添加按钮以及背景色以后效果

然后右键一直按住第一个按钮拖拽至第二个 VC (也就是黄色背景的)点击 show

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

实现第一个 VC 按钮点击方法

这时候两个 VC 之间就会出现一条线,然后点击线中间,设置 identifierPushSegue ,这里设置一个标识符,为后面的跳转做准备,效果如下:

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
设置 identifier

将两个按钮连接成 ViewController 的同一个属性,名为 popBtn ,然后将第二个 VC 的按钮实现一个点击方法(因为我们要 pop 回来)名为 popClick ,如下

import UIKit  class ViewController: UIViewController {      @IBOutlet weak var popBtn: UIButton!     override func viewDidLoad() {         super.viewDidLoad()         // Do any additional setup after loading the view, typically from a nib.     }      @IBAction func popClick(sender: AnyObject) {          self.navigationController?.popViewControllerAnimated(true)     }      override func didReceiveMemoryWarning() {         super.didReceiveMemoryWarning()         // Dispose of any resources that can be recreated.     }  }

最后,分别在两个 VC 的中间添加一个 imageView ,最后的效果图如下

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
最后效果图

如果到这里你还没错的话,那么运行一下你的工程,运行的效果将会是这样

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

最后的运行效果图

没错,也就是一个简单的 push 效果,现在准备工作已经做好了,想要实现放大效果的动画,还要继续往下进行。

三、开始实现放大效果

通过上面的步骤,我们已经做好了准备工作,我们还要知道的一点是,要想自定义导航的 pushpop 效果,需要实现 UINavigationControllerDelegate 协议里面的

   func navigationController(navigationController: UINavigationController,         interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {             return nil     }

这个协议方法,我们先新建一个继承与 NSObject 的名为 HWNavigationDelegate 的一个类,然后引入 UINavigationControllerDelegate ,实现上面的协议方法,使返回值暂时为 nil (从上面代码中可以看出返回值是一个可选值,所以这里可以先用 nil ,待会再具体实现)。然后你的 HWNavigationDelegate 里面的代码大致如下

// //  HWNavigationDelegate.swift //  HWAnimationTransition_Swift // //  Created by HenryCheng on 16/3/16. //  Copyright © 2016年 www.igancao.com. All rights reserved. //  import UIKit  class HWNavigationDelegate: NSObject, UINavigationControllerDelegate {      func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {         return nil;     } }

现在继续打开 storyboard ,然后在右下角搜索 Object ,并将其拖拽至左边 Navigation Controller Source 里,

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
添加 Object

并在选中 Object ,在右边将其类改成刚刚创建的 HWNavigationDelegate

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

HWNavigationDelegate.png

最后在左侧,点击 UINavigationController ,并将其 delegate 设置为刚才的 Object

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
设置导航的 delegate

现在上面 HWNavigationDelegate 里面导航的协议方法的返回值还是 nil ,我们需要创建一个实现动画效果的类,并使其返回,这里我们新建一个同样继承于 NSObject 的名为 HWTransitionAnimator 的类,并使其实现 UIViewControllerAnimatedTransitioning 协议,和其中的协议方法,为了便于阅读,这里贴出所有的代码,

// //  HWTransitionAnimator.swift //  HWAnimationTransition_Swift // //  Created by HenryCheng on 16/3/16. //  Copyright © 2016年 www.igancao.com. All rights reserved. //  import UIKit  class HWTransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {     weak var transitionContext: UIViewControllerContextTransitioning?      func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {          return 0.5     }      func animateTransition(transitionContext: UIViewControllerContextTransitioning) {          self.transitionContext = transitionContext          let containerView = transitionContext.containerView()         let fromVC = transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey) as! ViewController         let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) as! ViewController         let button = fromVC.popBtn          containerView?.addSubview(toVC.view)          let circleMaskPathInitial = UIBezierPath(ovalInRect: button.frame)         let extremePoint = CGPoint(x: button.center.x - 0, y: button.center.y - CGRectGetHeight(toVC.view.bounds))         let radius = sqrt((extremePoint.x * extremePoint.x) + (extremePoint.y * extremePoint.y))         let circleMaskPathFinal = UIBezierPath(ovalInRect: CGRectInset(button.frame, -radius, -radius))          let maskLayer = CAShapeLayer()         maskLayer.path = circleMaskPathFinal.CGPath         toVC.view.layer.mask = maskLayer          let 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     }  }

关于上面的所有代码,其中 func animateTransition(transitionContext: UIViewControllerContextTransitioning)func animateTransition(transitionContext: UIViewControllerContextTransitioning) 分别是设置时间和动画过程的方法,都是 UIViewControllerAnimatedTransitioning 的协议方法, func animationDidStop 是实现动画结束后的操作,这里动画结束后需要做取消动画和将 fromViewController 释放掉的操作。

里面的

        let circleMaskPathInitial = UIBezierPath(ovalInRect: button.frame)         let extremePoint = CGPoint(x: button.center.x - 0, y: button.center.y - CGRectGetHeight(toVC.view.bounds))         let radius = sqrt((extremePoint.x * extremePoint.x) + (extremePoint.y * extremePoint.y))         let circleMaskPathFinal = UIBezierPath(ovalInRect: CGRectInset(button.frame, -radius, -radius))          let maskLayer = CAShapeLayer()         maskLayer.path = circleMaskPathFinal.CGPath         toVC.view.layer.mask = maskLayer

这段代码,下面第二段代码的 maskLayer 这个上面开始的时候就说过了,第一段代码其实就是一个计算的过程,求出最后大圆效果的半径,原理如图(粗糙的画了一下,画得不好见谅^ _ ^)

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

动画效果关键的实现原理图

最后将刚才 HWNavigationDelegate 里的协议方法返回值修改成 HWTransitionAnimator 的对象就可以了

    func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {          return HWTransitionAnimator()     }

如果上面步骤,你操作没错的话,运行工程效果如下

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
tap_swift.gif

四、添加手势引导动画

添加手势实现动画效果,我们在刚才的 HWNavigationDelegate 类里实现 UINavigationControllerDelegate 的另外一个斜一方法

    func navigationController(navigationController: UINavigationController,         interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {             return self.interactionController     }

这里的 self.interactionController 就是我们的导航控制器,如下图

一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
设置导航属性

然后重写 awakeFromNib() 方法,关于整个 HWNavigationDelegate 最后的代码实现,如下

// //  HWNavigationDelegate.swift //  HWAnimationTransition_Swift // //  Created by HenryCheng on 16/3/16. //  Copyright © 2016年 www.igancao.com. All rights reserved. //  import UIKit  class HWNavigationDelegate: NSObject, UINavigationControllerDelegate {      @IBOutlet weak var navigationController: UINavigationController!     var interactionController: UIPercentDrivenInteractiveTransition?      func navigationController(navigationController: UINavigationController,         interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {             return self.interactionController     }      func navigationController(navigationController: UINavigationController, animationControllerForOperation operation: UINavigationControllerOperation, fromViewController fromVC: UIViewController, toViewController toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {          return HWTransitionAnimator() //        return nil;     }      override func awakeFromNib() {         super.awakeFromNib()         let panGesture = UIPanGestureRecognizer(target: self, action: Selector("panned:"))         self.navigationController.view.addGestureRecognizer(panGesture)     }      func panned(gestureRecognizer: UIPanGestureRecognizer) {         switch gestureRecognizer.state {         case .Began:              self.interactionController = UIPercentDrivenInteractiveTransition()             if self.navigationController?.viewControllers.count > 1 {                 self.navigationController?.popViewControllerAnimated(true)             } else {                 self.navigationController?.topViewController!.performSegueWithIdentifier("PushSegue", sender: nil)             }         case .Changed:              let translation = gestureRecognizer.translationInView(self.navigationController!.view)             let completionProgress = translation.x / CGRectGetWidth(self.navigationController!.view.bounds)             self.interactionController?.updateInteractiveTransition(completionProgress)         case .Ended:              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         }     } }

这里需要注意的是 gestureRecognizer 的几个状态

  • 1、 Begin :手势被识别时时,初始化 UIPercentDrivenInteractiveTransition 实例对象和设置属性,比如如果是第一个 VC 就实现 push ,反之则是 pop
  • 2、 Changed :开始手势到结束手势的一个过程,上面代码中是根据偏移量改变 self.interactionController 的位置
  • 3、 Ended :手势结束以后的操作,设置动画结束或者取消动画,最后将 self.interactionController 置为 nil
  • 4、 default :其他的状态
    运行你的工程,拖拽屏幕时效果如下
一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)
pan_swift.gif

五、最后

由于最近工作比较忙,好久没有写博客了,趁着这回功夫将这个小动画分享一下,希望大家喜欢,时间不早了,该回去休息了(在公司加班完成的,喜欢的就 star 一下吧),最后,这里只是 swift 版本的代码,同时如果你需要全部代码的话,你可以在下面下载

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » 一步步教你实现类似于格瓦拉启动页中的放大转场动画(objective-C && Swift)

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
分享按钮