"用过格瓦拉电影,或者其他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. 仿照CREATE_FUNC实现CCLayer中的返回CCScene* 的静态函数,宏包装成CREATE_SCENE(XXLayer)

    #define CREATE_SCENE(__TYPE__)\ CCScene *scene()\ { CCScene *scene=CCScene::create();\ __TYPE__ *lay ...

  2. Iphone5s 通话质量差 问题解决

    从某段时间开始,我的手机出现通话质差的情况.而且与信号电平有密切关系,只要覆盖稍差的地方,通话就劣化. 具体症状是我时常听不清对方说话,但对方能够清晰听到我说话. 因为本人也是从事无线网络优化软件产品 ...

  3. maven安装报错

    今天在安装maven时安照说明配置环境变量,通过命令检查: mvn -v 竟然出现以下错误,很郁闷的是我明明配置了JAVA_HOME,并且别的依赖java的东西都能用,通过java -version也 ...

  4. Unity3D学习笔记——递归+非递归遍历GameObject的子物体

    在Unity3D中没有提供直接的方法获取某个GameObject的子GameObject,但是所有的GameObject都有transform对象,所以,一般是通过获取子GameObject的tran ...

  5. 【Construct Binary Tree from Preorder and Inorder Traversal】cpp

    题目: Given preorder and inorder traversal of a tree, construct the binary tree. Note:You may assume t ...

  6. Android中的单位及测试相关概念

    android中的单位: in 英寸 pt 点距 px 像素  dp(dip) 密度无关的像素单位,自适应device屏幕的比例,通常涉及长宽高时采用 sp 与范围无关的像素单位,通常在设置字体大小时 ...

  7. 04.spring-data-redis与Jedis整合使用

    1.spring-data-redis与Jedis简单整合 spring-data-redis与Jedis简单整合,Redis没有任何集群只是单节点工作,使用连接池 1.创建spring-contex ...

  8. 【BZOJ】【1053】【HAOI2007】反素数ant

    搜索 经典搜索题目(其实是蒟蒻只会搜……vfleaking好像有更优秀的做法?) 枚举质数的幂,其实深度没多大……因为$2^32$就超过N了……而且质数不能取的太大,所以不会爆…… /******** ...

  9. NYOJ-235 zb的生日 AC 分类: NYOJ 2013-12-30 23:10 183人阅读 评论(0) 收藏

    DFS算法: #include<stdio.h> #include<math.h> void find(int k,int w); int num[23]={0}; int m ...

  10. GS界面上显示的重要参考数据

    GS界面上显示的重要参考数据,这个是压测时重要参考 struct GSinfo { int revBuffNum; int sendBuffNum; int clientNum; int dbAskN ...