文档章节

轻松学习之二——iOS利用Runtime自定义控制器POP手势动画

 北方人在上海
发布于 2016/07/23 11:37
字数 2813
阅读 5
收藏 0
点赞 0
评论 0

前言

苹果在IOS7以后给导航控制器增加了一个Pop的手势,只要手指在屏幕边缘滑动,当前的控制器的视图就会跟随你的手指移动,当用户松手后,系统会判断手指拖动出来的大小来决定是否要执行控制器的Pop操作。

nav_pop_origin.gif

这个操作的想法非常好,但是系统给我们规定的范围必须是屏幕左侧边缘才可以触发,这样实际使用过程中对于有些产品会产生不便,于是有些app就采取整个屏幕都响应这个手势并且pop动画还是用系统原生的,这样操作起来确实方便好多。

nav_pop_custom.gif

开始大家一定会有疑问,给控制器的View加个手势然后拖动控制器的View时改变它的frame不就可以了吗?没错,加手势这个想法是正确的。但是,由我们自己来改变控制器视图的位置是比较麻烦的,细心的朋友一定发现了,我们自定义pop手势上面的导航栏也是在随着你的手势拖拽而变动的,所以这样做还需要负责导航栏的动画,而且有一个重点问题,如果单独拖动view,这个view下面会是黑黑的一片,因为控制器的push和pop层级是由系统管理的。

nav_pop_failed.gif

所以走这条路虽然可以,但实现起来会比较艰辛。那么,如何实现这个效果呢?今天就给大家提供两套实现方案。


[1]

方案一:自定义UIViewControllerInteractiveTransitioning对象,实现导航控制器代理方法。

这个是苹果官方推荐的做法,在WWDC 2013 218 - Custom Transitions Using View Controllers中有说明。

这套方案虽然实现比较麻烦,但是动画相对灵活,你可以实现这样的效果,

nav_pop_cube.gif

也可以有这种效果。

nav_pop_flip.gif

其实这个拖动过程属于导航控制器的动画,所以我们需要重写UINavigationController的两个代理方法,navigationController:animationControllerForOperation:fromViewController:toViewController:(名字很长下面就称为方法1)和
navigationController:interactionControllerForAnimationController:(方法2)。
解释一下他们的作用,方法1是苹果提供给我们用来重写控制器之间转场动画的(pop或者push)。方法2你可以这样理解,苹果让我们返回一个交互的对象,用来实时管理控制器之间转场动画的完成度,通过它我们可以让控制器的转场动画与用户交互(注意一点,如果方法1返回是nil,方法2是不会调用的,也就是说,只有我们自定义的动画才可以与控制器交互)。

下面我们来看一下实现过程。为了便于大家理解,我会尽量在Demo中的注释写的最清晰明了。
同时,我们先用最简单的代码实现,在这篇文章的最后我会对本例中的Demo提供一个相对合理的写法。

首先在方法1中,我们返回一个遵守了UIViewControllerAnimatedTransitioning协议的对象,它就是自定义的动画对象,我们给它起名PopAnimation,在这个类中实现两个方法来自定义转场动画。

屏幕快照 2015-03-28 下午6.49.05.png

再来看方法2,我们需要返回一个遵守了UIViewControllerInteractiveTransitioning协议的对象(提示一下,这两个协议容易混淆,要注意区分,一个是负责动画,一个是负责交互过程),苹果已经有一个类专门处理这个功能,它叫UIPercentDrivenInteractiveTransition,当然你也可以自定义一个这样的类。我们可以这样理解它的作用:前面在方法1中返回的动画,会在执行的过程中被系统分解以用于用户交互,这个交互过程的动画完成度就由它来调控。下面我们来看一下如何使用它。(为了让控制器视图拖动,我们给控制器的视图加了一个拖动手势,在拖动方法里我们对这个对象进行操作)

屏幕快照 2015-03-29 下午12.33.59.png

最后在视图控制器里重写导航栏的两个方法。

屏幕快照 2015-03-29 下午12.37.51.png

有两点不要忘记:

  1. 设置导航控制器的代理为当前控制器。
  2. 给控制器加手势。

OK,这样我们就完成了这个过程。

nav_pop_own.gif


[2]

方案二:Runtime+KVC

要了解这样的做法,需要有Runtime的一些知识,会涉及到私有变量、私有方法的获取,但是这样做比较简单也比较有趣,如果你感兴趣就继续看下去吧。关于Runtime的知识,今后我会分享到博客里,朋友们敬请期待。

为了方便大家阅读下面的代码,我们需要先了解系统的这个手势。

前面我们了解到,这个手势属于UINavigationController,我们就跳到它的头文件里看看能不能找到线索。这个思路是正确的,确实有一个手势叫做interactivePopGestureRecognizer。属性为readonly,就是说我们不能给他换成自定义的手势,但是可以设置enable=NO。ok,既然找到了它,就打印一下看看它到底是一个什么手势。

屏幕快照 2015-03-26 下午5.17.35.png

通过log,我们看到他属于UIScreenEdgePanGestureRecognizer这个类(之前我是没有用到过),它继承自UIPanGestureRecognizer,出现在IOS7以后,是专门处理在屏幕边缘触发的手势类型,并且只有一个属性叫edges,用来设置它的触发边缘(上、下、左、右、全部)。看到这里一些朋友会想,直接改它的edges为全部可不可以?经过试验了解到,改这个属性是没用的,它只能用来触发边缘,设为全部的意思是四个方向的边缘会触发,而且用来做控制器POP手势的只有左边缘。

我们继续看它的log。控制台除了打印了它的类,还打印了它的触发target:_UINavigationInteractiveTransition(这是一个私有类,看来是专门用来做导航控制器交互动画的),和action:handleNavigationTransition(这是它的一个私有方法),我们要做的就是新建一个UIPanGestureRecognizer,让它的触发和系统的这个手势相同,这就需要利用runtime获取系统手势的target和action。

那么如何获取这个target呢?一开始我用kvc想直接获取这个手势的target,程序崩溃了,原来它根本没有这样一个属性。所以我能想到的是,先利用runtime遍历它的所有成员变量,看看系统是怎么存储这个属性的,

屏幕快照 2015-03-29 下午3.25.02.png


通过log我们可以看到,UIGestureRecognizer有一个叫_targets的属性,它的类型为NSMutableArray。

屏幕快照 2015-03-29 下午3.25.09.png


它是用数组来存储每一个target-action,所以可以动态的增加手势触发对象。那么又是什么存储每一个target-action呢?为了了解这个我们拿到这个属性的名字"_targets"通过kvc获取它,接着打印出来。

屏幕快照 2015-03-29 下午3.33.54.png

屏幕快照 2015-03-29 下午3.34.01.png

可以看到,由于系统重写了它的description方法,所以我们没办法通过打印获取这个对象是什么类型。既然不能打印,那么我们就用断点调试,来看它的真实类型,

屏幕快照 2015-03-29 下午3.37.32.png

我们看到,原来每一个target-action是用UIGestureRecognizerTarget这样一个类来存储的,它也是一个私有类。
苹果把许多的类做私有化也是有原因所在,其实在平时我们拿到这个类也是没有用的,他们的目的之一是避免对开发者公开无用的类,影响了封装性。所以在类的设计上,还是要向苹果学习。

下面直接看代码。

我们在控制器的ViewDidLoad加上这段代码,并且它只需要执行一次。

屏幕快照 2015-03-29 下午4.07.48.png


优化

这个demo我会提供给大家,下面简单说下程序的优化思路。

  • 优化点一:对于方案一,其实不应该把导航控制器的代理方法以及手势处理的方法交给视图控制器,因为这段代码不是属于某一个视图控制器,而是全局的导航控制器,所以我们应该参考苹果的设计思想:新建一个专门管理交互过程的对象,这个类我们叫做NavigationInteractiveTransition。

  • 优化点二:再来看之前的ViewDidLoad中只执行一次的代码,其实写在这里也不够妥当,同样的,这段代码也不属于某一个Controller,优化方案是新建一个导航控制器,在这个导航控制器的viewDidLoad中写上这些代码,这样也并不需要dispatch once。

  • 优化点三:由于我们自定义的手势是加在一个私有view上,这个view是一个全局的,所以当这个控制器为根控制器时,我们的手势还是在起作用,这就相当于对根控制器做了pop操作,这会出现一个错误nested pop animation can result in corrupted navigation bar。导致这个错误的原因还有一个,如果我们pop的动画正在执行,再去触发一次手势,会导致导航控制器和导航条的动画混乱。为了避免问题出现我们需要成为手势的代理,判断当前控制器是否为根控制器并且pop或者push动画是否在执行(这个变量是私有的,需要用kvc来获取)。

    屏幕快照 2015-03-30 下午5.06.24.png

经过最后的优化,视图控制器可以什么都不写,想使用这个效果,只要使用我们自定义的导航控制器就可以了,这样的好处是手势动画与控制器完全解耦,并且不用给每一个控制器都addGesture。


给大家推荐一个仓库https://github.com/nst/iOS-Runtime-Headers,这个仓库可以调取苹果的所有私有方法头文件,相当强大。

最后放上这个demo的地址:https://github.com/zys456465111/CustomPopAnimation(使用时,切换工程的scheme就能切换不同方案。对于方案二,只需要导航控制器的类就可以了。)

感谢大家,轻松学习系列还会继续下去,我会尽量写出更多通俗易懂的文章,让开发变得轻松起来,我的微博:http://weibo.com/JazysYu


 

文/J_雨(简书作者)
原文链接:http://www.jianshu.com/p/d39f7d22db6c
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

© 著作权归作者所有

共有 人打赏支持
粉丝 22
博文 187
码字总数 265578
作品 0
浦东
全屏手势返回插件 - BBGestureBack

BBGestureBack Full screen return gesture(全屏手势返回 滑动返回 pop 动画效果) iOS 侧滑返回 BBGestureBack iOS 全屏手势返回 滑动返回 pop 动画效果 现在市场上的主流app都有用到各种侧...

Bonway
05/30
0
0
iOS高仿QQ侧滑控件、下载框架、动画效果、扫一扫、颜色变化、K线图等源码

iOS精选源码 仿京东"加入购物车"转场动画(http://www.code4app.com/thread-28162-1-1.html) ColorTool(颜色转换)(http://www.code4app.com/thread-29256-1-1.html) Swift 专业版K线(http://w......

sunnyaigd
04/17
0
0
UIView Animations 学习笔记(一)简介及API说明

目录 UIView Animations 学习笔记(一)简介及API说明 UIView Animation 学习笔记 应用示例 一、简介 随着iOS7的发布,动画与运动效果变成了Apple和第三方开发者进行app设计的中心。iOS7介绍...

灰大羊
2015/08/01
0
0
iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用

iOS开发中的手势体系——UIGestureRecognizer分析及其子类的使用 一、引言 在iOS系统中,手势是进行用户交互的重要方式,通过UIGestureRecognizer类,我们可以轻松的创建出各种手势应用于app...

珲少
2015/11/06
0
0
那些在学习iOS开发前就应该知道的事(part 2)

英文原文:Things I wish I had known before starting iOS development—Part 2 如果你还没读这篇文章的第一部分,请先读完了再来看第二部分。 那些在学习iOS开发前就应该知道的事(part 1)...

TomatosX
2015/06/12
0
0
iOS一些代码的取巧写法总结(二)

一、在xib/storyboard里面设置view圆角半径 在xib/storyboard里面设置view圆角半径 神奇的IB_DESIGNABLE和IBInspectable(xib中设置圆角) 二、push pop 动画突然消失的解决方案 产生的原因是动...

朝雨晚风
2016/12/05
0
0
UIView Animations 学习笔记(二)定制动画效果及切换效果

目录 UIView Animations 学习笔记(一)简介及API说明 UIView Animations 学习笔记(二)定制动画效果及切换效果 UIView Animation 学习笔记 应用示例 二、视图切换定制 2.1 基础说明 要创建...

灰大羊
2015/08/12
0
0
iOS商城demo、音乐播放器、视频通话、自定义搜索、转场动画等源码

iOS精选源码 微信自定义搜索框实现(http://www.code4app.com/thread-13182-1-1.html) 一个商城Demo,持续更新中(http://www.code4app.com/thread-13957-1-1.html) 在Object-C中学习数据结构与...

sunnyaigd
07/03
0
0
转:一套代码iOS、Android两端运行,Google Flutter意味着什么?

原文:https://www.toutiao.com/a6569388465538990600/?ttfrom=weixin&utmcampaign=clientshare×tamp=1529580533&app=newsarticle&utmsource=weixin&iid=35476649324&utmmedium=toutiao......

鸿蒙无上至尊
06/22
0
0
论坛源码推荐(4月28日):列表项目的波浪效果 兼容iOS 6与iOS 7的自定义导航条

Flow 基于手势的教程框架,以用户熟悉的手势来驱动用户界面。灵感来自于Facebook Paper。 测试环境:Xcode 5.0,iOS 4.3以上 AMWaveTransition 带有表格的视图控制器切换效果,点击每个栏目会...

mingxun
2014/04/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

OSChina 周一乱弹 —— 你的朋友圈有点生锈了

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @Devoes :分享Trademark的单曲《Only Love (电视剧《妙手仁心 II》插曲)》: 《Only Love (电视剧《妙手仁心 II》插曲)》- Trademark 手机党少...

小小编辑
今天
203
9
【面试题】盲人坐飞机

有100位乘客乘坐飞机,其中有一位是盲人,每位乘客都按自己的座位号就坐。由于盲人看不见自己的座位号,所以他可能会坐错位置,而自己的座位被占的乘客会随便找个座位就坐。问所有乘客都坐对...

garkey
今天
1
0
谈谈神秘的ES6——(二)ES6的变量

谈谈神秘的ES6——(二)ES6的变量 我们在《零基础入门JavaScript》的时候就说过,在ES5里,变量是有弊端的,我们先来回顾一下。 首先,在ES5中,我们所有的变量都是通过关键字var来定义的。...

JandenMa
今天
1
0
arts-week1

Algorithm 594. Longest Harmonious Subsequence - LeetCode 274. H-Index - LeetCode 219. Contains Duplicate II - LeetCode 217. Contains Duplicate - LeetCode 438. Find All Anagrams ......

yysue
今天
2
0
NNS拍卖合约

前言 关于NNS的介绍,这里就不多做描述,相关的信息可以查看NNS的白皮书http://doc.neons.name/zh_CN/latest/nns_background.html。 首先nns中使用的竞价货币是sgas,关于sgas介绍可以戳htt...

红烧飞鱼
今天
1
0
Java IO类库之管道流PipeInputStream与PipeOutputStream

一、java管道流介绍 在java多线程通信中管道通信是一种重要的通信方式,在java中我们通过配套使用管道输出流PipedOutputStream和管道输入流PipedInputStream完成线程间通信。多线程管道通信的...

老韭菜
今天
0
0
AB 压力测试

Ubuntu 安装AB apapt-get install apache2-utils 使用AB 压力测试 -c 并发数 -n请求总数 ab -c 3000 -n 10000 http://localhost/test/index.php AB只能测试localhost 返回结果 This is Apac......

xiawet
今天
0
0
用Python绘制红楼梦词云图,竟然发现了这个!

Python在数据分析中越来越受欢迎,已经达到了统计学家对R的喜爱程度,Python的拥护者们当然不会落后于R,开发了一个个好玩的数据分析工具,下面我们来看看如何使用Python,来读红楼梦,绘制小...

猫咪编程
今天
1
0
Java中 发出请求获取别人的数据(阿里云 查询IP归属地)

1.效果 调用阿里云的接口 去定位IP地址 2. 代码 /** * 1. Java中远程调用方法 * http://localhost:8080/mavenssm20180519/invokingUrl.action * @Title: invokingUrl * @Description: * @ret......

Lucky_Me
今天
1
0
protobuf学习笔记

相关文档 Protocol buffers(protobuf)入门简介及性能分析 Protobuf学习 - 入门

OSC_fly
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部