一个丝滑的全屏滑动返回手势

原文地址:一个丝滑的全屏滑动返回手势

全屏返回手势

自 iOS7 之后,Apple 增加了屏幕边缘右划返回交互的支持,再配合上 UINavigationController 的交互式动画,pop 到上一级页面的操作变的非常顺畅和丝滑,从此,我很少再使用点击左上角导航栏上的返回按钮的方式返回了,因为这对单手操作十分不友好;如果一个 App 居然胆敢不支持滑动返回,那离被卸载就不远了。

说到全屏返回手势,首先我感觉这件事本身可能就有问题,毕竟有点反苹果官方的交互,让用户从任意的地方都能够滑动返回这个交互在国内的 App 中非常普遍,比如我手机中的手Q、微博、网易新闻、大众点评等,当然还有百度知道- -。这里得对微信的产品经理们得点个赞,从整个 App 来看,不论是交互还是 UI 结构和样式都非常的 iOS,没有什么特别奇葩的页面和交互,以至于使用 UIKit 原生的框架可以非常简单的搭建起来,这也符合我个人对 App 的一个愿景:一个优秀的 App 不论从用户角度看还是从代码角度看都应该是简单且优雅的,呼吁各家产品经理可以多借鉴下像微信这样很本色的 App 设计。(以后可以分享下如何使用 Storyboard 在一小时内快速搭建起微信 UI)

FDFullscreenPopGesture

工作毕竟是工作,于是乎所以就被迫实现了套 pan 手势处理加截图和视差,虽然在运动曲线上、bar 截图处理上下了不少功夫,但距离系统的丝滑效果还是差距挺远。随时间推移,终于能够最低支持 iOS7 后,我们把这个问题再次拿出来讨论和研究,直到在微博上看到了J_雨同学的这篇文章后才找到了这个迄今为止最简单的解决方案。于是乎在他的授权下,我们在 forkingdog 上把这个返回手势开源,github地址,并果断应用到了百度知道 App 内,这是 Demo 效果:

利用了系统自己的边缘返回手势处理函数后,一切动画和曲线都和原生效果一毛一样了。

于是乎发布了FDFullscreenPopGesture1.0 版本,而且提供了一个 AOP 形式的 API,把它添加到工程里面,什么代码都不用写,所有 UINavigationController 就自带这个全屏返回效果了。

丝滑的处理导航栏的显示和隐藏

接下来我们发现利用系统的 UINavigationBar 时,返回手势中若碰到前一个页面有 bar,后一个页面没 bar,或者反过来时,动画就非常难看,举两个反例:

手Q iOS:

它的个人中心页面上面的 bar 是隐藏状态,然后做了个和其他页面很像的假 bar,但返回手势一开始就露馅了,为了弥补,还做了下后面真 bar 的 alpha 值动画,两个返回按钮还是重叠在了一起。

新浪微博 iOS:

和手Q一样的实现方式,只不过没做 alpha 动画,所以就非常明显了。

为啥会这样呢?这可能就是 UINavigationController 在导航栏控制 API 上设计的缺陷了。 一个

UINavigationController 管理了串行的 N 个 UIViewController 栈式的 push 和 pop,而

UINavigationBar 由 UINavigationController 管理,这就导致了 UIViewController

无法控制自己上面的 bar 单独的隐藏或显示。 这非常像 UIApplication 全局的 status bar,牵一发还得动全身,不过

Apple 在 iOS7 之后为 vc 控制自己的 status bar 提供了下面几个方法:

- (UIStatusBarStyle)preferredStatusBarStyleNS_AVAILABLE_IOS(7_0);

- (BOOL)prefersStatusBarHiddenNS_AVAILABLE_IOS(7_0);

- (UIStatusBarAnimation)preferredStatusBarUpdateAnimationNS_AVAILABLE_IOS(7_0);

终于让这个全局变量变成了局部变量,虽然写起来费劲了些。

但是对 UINavigationBar 的控制,依然是全局的,可能 Apple 觉得 App 不应该有这种奇怪的页面结构?

解决这个问题的方法也不难,在滑动返回的后要出现的那个 view controller 中写下面的代码:

- (void)viewWillAppear:(BOOL)animated {

[superviewWillAppear:animated];

[self.navigationController setNavigationBarHidden:YESanimated:animated];

}

系统就会把有 bar 和 无 bar 的 transition 动画衔接起来。但是如上面所说,这是个全局变量,还得在所有由这个没有 bar

的特殊页面能 push 和 pop 的页面都进行反向的处理,代码非常的乱乎。于是乎,我们试着解决了这个问题,先看效果:

我特意挑了个从真 bar 到假 bar,再从假 bar 到 真 bar 的页面,还算蛮丝滑的,transition 动画全是系统自己搞定的。

就事把FDFullscreenPopGesture更新到了 1.1 版本,贯彻我们一向的精简 API,你只需要在 bar 要隐藏的 view controller 中写一句话:

- (void)viewDidLoad

[superviewDidLoad];

self.navigationController.fd_prefersNavigationBarHidden =YES;

}

或者喜欢重载的写法也行:

- (BOOL)fd_prefersNavigationBarHidden {

returnYES;

}

刻意的模仿了下系统的命名风格,就这一句话,剩下的就都不用操心了。

关于私有API

大家会质疑说,这用到了 UIKit 的私有属性和私有 API,要是系统升级变了咋办?要是审核被拒了咋办?

首先,iOS 系统的 SDK 为了向下兼容,一般只会增加方法或者修改方法实现,不太可能直接删除一个共有方法,而私有方法的行为确实可能有变化,但系统 release 频率毕竟很低,每当新版本发布时 check 下原来的功能是否能 work 就好了,大可不必担心这么远,SDK 是死的人是活的。

另一个就是审核问题,FDFullscreenPopGesture 的实现中有主要有两处触碰到了私有 API:

// 1. 私有变量标志transition动画是否正在进行

[self.navigationController valueForKey:@"_isTransitioning"];

// 2. 一个内部的selector

NSSelectorFromString(@"handleNavigationTransition:");

不论是 kvc 还是 selector 反射,都是利用 objc runtime

完成的,而到了这一层,真的就没啥公有私有可言了。设想你就是开发 Apple 私有 API 检查工具的工程师,给你一个 ipa

的包,你会如何检查出其中有没有私有 API 呢?

首先,这个检查一定是个静态检查吧,不可能是运行时检查,因为代码逻辑那么复杂,把程序跑起来看所有 objc_msgSend 中包不包括私有调用这件事太不现实了。

对 ipa 文件做静态检查的话肯定是去分析 Mach-O 可执行文件,因为这时很多源代码级别的信息已经丢失,经分析可以采取下面几种手段:

是否 link 了私有 framework 或者公开 framework 中的私有符号,这可以防止开发者把私有 header 都 dump 出来供程序直接调用。

同上,使用@selector(_private_sel)加上-performSelector:的方式直接调用私有 API。

扫描所有符号,查看是否有继承自私有类,重载私有方法,方法名是否有重合。

扫描所有string,看字符串常量段是否出现和私有 API 对应的。

我觉得前三条被 catch 住的可能性最高,也最容易被检查出来。再来看我们用到用字符串的方法 kvc 和 反射 selector,应该属于最后一条,这时候就很难抉择了,拿handleNavigationTransition:来说,看上去人畜无害啊,我自己类里面的方法也完全可能命名出这个来,所以单单凭借字符串命中私有 API 判定,苹果很容易误伤一大票开发者。

综上,我觉得使用字符串的方式使用私有 API 是相对安全的,我们的 App 马上要提交审核,如果过了几天你还能读到这段文字,说明我的猜想是木有错的,大家可以放心使用。

0 代码的 Demo

还有一个有意思的事,我们在 github 上的demo工程木有写一行代码,就实现了下面的效果:

工程长这个样子,view controller 类也没写,为了体现FDFullscreenPopGesture的 AOP 性质:

页面由 Storyboard 构建:

而控制页面隐藏 bar 的属性也能用 Runtime Attributes 模拟调用:

这样就完成了一个非常干净的 Demo

加入到你的工程中

首先要求最低支持 iOS7,我想在 WWDC 2015 结束,iOS9 发布后,主流的 App 就都会 iOS7 起跳了。

依然是熟悉的 cocoapods 安装:

pod 'FDFullscreenPopGesture', '~> 1.1'

要是没有搜到就pod setup下。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,527评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,314评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,535评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,006评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,961评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,220评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,664评论 3 392
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,351评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,481评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,397评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,443评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,123评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,713评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,801评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,010评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,494评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,075评论 2 341

推荐阅读更多精彩内容

  • 来源:blog.sunnyxx.com/2015/06/07/fullscreen-pop-gesture/ 全屏...
    看之学之阅读 462评论 0 0
  • 全屏返回手势 本文章摘自于:一个丝滑的全屏滑动返回手势 自 iOS7 之后,Apple 增加了屏幕边缘右划返回交互...
    许威彬阅读 2,133评论 4 9
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 11,983评论 4 60
  • 轻轻提笔 写下与你满心欢喜 微微白头 认真与你携手到老
    张轩轩呢阅读 287评论 2 0
  • 医生有三重境界,第一重叫治病救人,你能够看好病人的疾病,这只能说明你是一个医务工作者,一个技工,和修鞋匠、卖馒头发...
    柳叶刀zhao阅读 2,344评论 0 2