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

本文主讲SWIFT版,OC版在后面会留下Demo下载

在iOS中,在同一个导航控制器你可以自定义转场动画实现两个viewController之间的过渡。实际上在iOS7之后,通过实现

UIViewControllerAnimatedTransitioning或者UIViewControllerContextTransitioning协议,

就可以简单的自定义转场动画,比如一个NavigationController的push和pop。还有一点你需要知道的是,我如果有一个矩形, 有一个圆,想要在这个矩形上剪出和圆大小相同的面积,那么就要用到CALayer的mask属性,下面用图表达可能会直观些:

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

开始实现简单的push效果

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


然后效果如下

把右侧的Shows Navigation Bar去掉,因为这个demo里面并不需要导航栏,同时保证Is Instal View Controller是被勾上的(不知道的童鞋可以去掉看一下效果),这里默认的都是勾选上的。然后在新建一个viewController,并设置其继承于ViewController,如下

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

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

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

将两个按钮连接成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,最后的效果图如下

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

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

开始实现放大效果

通过上面的步骤,我们已经做好了准备工作,我们还要知道的一点是,要想自定义导航的push或pop效果,需要实现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里,

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


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

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

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

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

    return HWTransitionAnimator()
}

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

添加手势引导动画

添加手势实现动画效果,我们在刚才的<br>
HWNavigationDelegate类里实现
UINavigationControllerDelegate的另外一个斜一方法
func navigationController(navigationController: UINavigationController,
interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
return self.interactionController
}

这里的

self.interactionController

就是我们的导航控制器,如下图

然后重写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:其他的状态运行你的工程,拖拽屏幕时效果如下

最后

iOS 开发--转场动画的更多相关文章

  1. IOS开发系列 --- 核心动画

    原始地址:http://www.cnblogs.com/kenshincui/p/3972100.html 概览 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥i ...

  2. iOS 自定义转场动画

    代码地址如下:http://www.demodashi.com/demo/12955.html 一.总效果 本文记录分享下自定义转场动画的实现方法,具体到动画效果:新浪微博图集浏览转场效果.手势过渡动 ...

  3. iOS 自定义转场动画浅谈

    代码地址如下:http://www.demodashi.com/demo/11612.html 路漫漫其修远兮,吾将上下而求索 前记 想研究自定义转场动画很久了,时间就像海绵,挤一挤还是有的,花了差不 ...

  4. iOS自定义转场动画实战讲解

    iOS自定义转场动画实战讲解   转场动画这事,说简单也简单,可以通过presentViewController:animated:completion:和dismissViewControllerA ...

  5. VCTransitionsLibrary –自定义iOS交互式转场动画的库

    简介 VCTransitionsLibrary 提供了许多适用于入栈,出栈,模态等场景下控制器切换时的转场动画.它本身提供了一个定义好的转场动画库,你可以拖到自己工程中直接使用;也提供了许多拥有不同转 ...

  6. iOS开发之核心动画(Core Animation)

    1.概述 Core Animation是一组非常强大的动画处理API,使用它能做出非常炫丽的动画效果,而且往往是事半功倍,使用它需要先添加QuartzCore.framework和引入对应的框架< ...

  7. iOS开发之各种动画各种页面切面效果

    因工作原因,有段时间没发表博客了,今天就发表篇博客给大家带来一些干货,切勿错过哦.今天所介绍的主题是关于动画的,在之前的博客中也有用到动画的地方,今天就好好的总结一下iOS开发中常用的动画.说道动画其 ...

  8. iOS开发 QQ粘性动画效果

    QQ(iOS)客户端的粘性动画效果 时间 2016-02-17 16:50:00  博客园精华区 原文  http://www.cnblogs.com/ziyi--caolu/p/5195615.ht ...

  9. 【转】iOS开发之各种动画各种页面切面效果

    原文: http://www.cnblogs.com/ludashi/p/4160208.html?utm_source=tuicool 因工作原因,有段时间没发表博客了,今天就发表篇博客给大家带来一 ...

随机推荐

  1. 25.usb固件深入

    dscr51里放的是USB描述符表,EZ-USB在重枚举阶段会读取或设置相应的描述符: db    DSCR_DEVICE_LEN          ;; Descriptor length db   ...

  2. 如何向VS2010中插入ActiveX控件并且附带相应的类

    上两篇文章中我们已经讲述了ActiveX控件的一些相关知识,本文中,简单说明一下如何在我们自己的程序中使用ActiveX控件.(仍以我们上节课的例子为例) 我们打开VS2010编辑器,新建一个基于对话 ...

  3. 为SM30视图分配事务代码

    Tcode:SE93

  4. OC中数组类NSArray的详解,数组的遍历(二)

    数组类的便利 1.for循环(大家都会的...) 2.NSEmunerator 3.for in 首先重点说下 第二种NSEmunerator枚举器,系统声明是 @interface NSEnumer ...

  5. 四则运算2扩展---c++

    题目:让程序能接受用户输入答案,并判定对错.最后给出总共对/错 的数量. 一.设计思想 1.存储用户输入答案,与正确答案比较得出总共对错数量. 二.源程序代码 #include<iostream ...

  6. mongodb修改器

    mongodb修改器 转载自:http://blog.csdn.net/mcpang/article/details/7752736 mongodb修改器(\(inc/\)set/\(unset/\) ...

  7. MVC 数据验证收集代码

    控制器 Home using System; using System.Collections.Generic; using System.Linq; using System.Web; using ...

  8. 常见的百度蜘蛛IP

    根据不同的IP我们可以分析网站是个怎样的状态, 以下常见的百度蜘蛛IP: 123.125.68.*这个蜘蛛经常来,别的来的少,表示网站可能要进入沙盒了,或被者降权. 220.181.68.*每天这个I ...

  9. Posix线程编程指南(1) 线程创建与取消

    线程创建 1.1 线程与进程 相对进程而言,线程是一个更加接近于执行体的概念,它可以与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列.在串行程序基础上引入线程和进程是为了提高程序的 ...

  10. 【BZOJ】【4002】【JLOI2015】有意义的字符串

    构造线性递推式+矩阵乘法 题解戳PoPoQQQ 为了自己以后看的方便手打一遍好了>_> 求$( \frac{b+\sqrt{d}}{2} )^n$的整数部分对p取模后的值 其中$b\mod ...