iOS7下滑动返回与ScrollView共存二三事

【转载请注明出处】 =
=不是整篇复制就算注明出处了亲。。。

iOS7下滑动返回与ScrollView共存二三事

【前情回顾】

去年的时候,写了这篇帖子iOS7滑动返回。文中提到,对于多页面结构的应用,可以替换interactivePopGestureRecognizer的delegate以统一管理应用中所有页面滑动返回的开关,比如在UINavigationController的派生类中


 1 //我是一个NavigationController的派生类
2 - (id)initWithRootViewController:(UIViewController *)rootViewController
3 {
4 self = [super initWithRootViewController:rootViewController];
5 if (self)
6 {
7 //在naviVC中统一处理栈中各个vc是否支持滑动返回的情况
8 //当前仅最底层的vc关闭滑动返回
9 self.interactivePopGestureRecognizer.delegate = (id<UIGestureRecognizerDelegate>)self;
10 }
11
12 return self;
13 }

然后在委托中控制滑动返回的开关


 1 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
2 {
3 if (self.viewControllers.count == 1)//关闭主界面的右滑返回
4 {
5 return NO;
6 }
7 else
8 {
9 return YES;
10 }
11 }

【问题所在】

看上去挺美好的。。。。直到遇到了ScrollView。

替换了delegate后,在使用时ScrollView,在屏幕左边缘就无法触发滑动返回效果了,如图

【问题原因】

滑动返回事实上也是由存在已久的UIPanGestureRecognizer来识别并响应的,它直接与UINavigationController的view(方便起见,下文中以UINavigationController.view表示)进行绑定,因此上图中存在如下关系:
UIPanGestureRecognizer
         ——bind——  UIScrollView

UIScreenEdgePanGestureRecognizer ——bind——
 UINavigationController.view

滑动返回无法触发,说明UIScreenEdgePanGestureRecognizer并没有接收到手势事件。

根据apple君的官方文档,UIGestureRecognizer和UIView是多对一的关系(具体点这里),UIGestureRecognizer一定要和view进行绑定才能发挥作用。因此不难想象,UIGestureRecognizer对于屏幕上的手势事件,其接收顺序和UIView的层次结构是一致的。同样以上图为例

(我是Z轴)-------------------------------------------------------------------------------------------------------------------------------------->

UINavigationController.view —>  UIViewController.view
—>  UIScrollView —>  Screen and User‘s finger

即UIScrollView的panGestureRecognizer先接收到了手势事件,直接就地处理而没有往下传递。

实际上这就是两个panGestureRecognizer共存的问题。

【解决方案】

苹果以UIGestureRecognizerDelegate的形式,支持多个UIGestureRecognizer共存。其中的一个方法是:


1 // called when the recognition of one of gestureRecognizer or otherGestureRecognizer would be blocked by the other
2 // return YES to allow both to recognize simultaneously. the default implementation returns NO (by default no two gestures can be recognized simultaneously)
3 //
4 // note: returning YES is guaranteed to allow simultaneous recognition. returning NO is not guaranteed to prevent simultaneous recognition, as the other gesture‘s delegate may return YES
5 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer;

一句话总结就是此方法返回YES时,手势事件会一直往下传递,不论当前层次是否对该事件进行响应。

看看UIScrollView的头文件,有如下描述:

1 // Use these accessors to configure the scroll view‘s built-in gesture recognizers.
2 // Do not change the gestures‘ delegates or override the getters for these properties.
3 @property(nonatomic, readonly) UIPanGestureRecognizer *panGestureRecognizer NS_AVAILABLE_IOS(5_0);

UIScrollView本身是其panGestureRecognizer的delegate,且apple君明确表明不能修改它的delegate(修改的时候也会有警告)。

那么只好曲线救国。

UIScrollView作为delegate,说明UIScrollView中实现了上文提到的shouldRecognizeSimultaneouslyWithGestureRecognizer方法,返回了NO。创建一个UIScrollView的category,由于category中的同名方法会覆盖原有.m文件中的实现,使得可以自定义手势事件的传递,如下:


 1 @implementation UIScrollView (AllowPanGestureEventPass)
2
3 - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
4 {
5 if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]
6 && [otherGestureRecognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]])
7 {
8 return YES;
9 }
10 else
11 {
12 return NO;
13 }
14 }

再次运行demo,看看效果:

嗯,滑动返回已经成功触发,鼓掌!

等会等会。。。

好像不太对。。。

scrollView怎么也滚动了!!!!!

O。。。只是做到了将手势事件往下传递,而没有关闭掉在边缘时UIScrollView对事件的响应。

事实上,对UIGestureRecognizer来说,它们对事件的接收顺序和对事件的响应是可以分开设置的,即存在接收链和响应链。接收链如上文所述,和UIView绑定,由UIView的层次决定接收顺序。

而响应链在apple君的定义下,逻辑出奇的简单,只有一个方法可以设置多个gestureRecognizer的响应关系:


// create a relationship with another gesture recognizer that will prevent this gesture‘s actions from being called until otherGestureRecognizer transitions to UIGestureRecognizerStateFailed
// if otherGestureRecognizer transitions to UIGestureRecognizerStateRecognized or UIGestureRecognizerStateBegan then this recognizer will instead transition to UIGestureRecognizerStateFailed
// example usage: a single tap may require a double tap to fail
- (void)requireGestureRecognizerToFail:(UIGestureRecognizer *)otherGestureRecognizer;

每个UIGesturerecognizer都是一个有限状态机,上述方法会在两个gestureRecognizer间建立一个依托于state的依赖关系,当被依赖的gestureRecognizer.state
= failed时,另一个gestureRecognizer才能对手势进行响应。

所以,只需要

[_scrollView.panGestureRecognizer requireGestureRecognizerToFail:screenEdgePanGestureRecognizer];

即可。

再次运行demo,看看效果:

Works like a Charm!!

P.S:screenEdgePanGestureRecognizer是和UINavigationController.view绑定的,因此可以遍历UINavigationController.view.gestureRecognizers来获取,如下:


- (UIScreenEdgePanGestureRecognizer *)screenEdgePanGestureRecognizer
{
UIScreenEdgePanGestureRecognizer *screenEdgePanGestureRecognizer = nil;
if (self.view.gestureRecognizers.count > 0)
{
for (UIGestureRecognizer *recognizer in self.view.gestureRecognizers)
{
if ([recognizer isKindOfClass:[UIScreenEdgePanGestureRecognizer class]])
{
screenEdgePanGestureRecognizer = (UIScreenEdgePanGestureRecognizer *)recognizer;
break;
}
}
}

return screenEdgePanGestureRecognizer;
}

demo地址:https://github.com/cDigger/CoExistOfScrollViewAndBackGesture/tree/master

【总结】

写了这么多,只是为了最初统一管理滑动返回的一点点便利,似乎很有些得不偿失。

我并不建议直接在项目中使用这种非常规手段。

但使用apple君提供的积木,自己拼出系统中的新功能,也是iOS开发的乐趣之一啊。

iOS7下滑动返回与ScrollView共存二三事,码迷,mamicode.com

时间: 2024-10-05 18:46:59

iOS7下滑动返回与ScrollView共存二三事的相关文章

iOS开发页面滑动返回跟scrollView左右划冲突问题

-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { // 首先判断otherGestureRecognizer是不是系统pop手势 if ([otherGestureRecognizer.view isKindOfClas

linux下苦逼搭建ftp站点二三事

昨天中午,领导突然扔给我一台linux服务器,让我给某个站点目录配置个ftp服务 权限可上传.可下载.令其无法跳转上级目录 我想这简单啊,于是我按照固定的四维,useradd.passwd.修改conf文件.修改权限,最后restart 结果就傻逼了,一直连接不上,报530错误,我更傻逼的做法一直在创建.删除用户 - -|| 在百度苦苦寻求答案的我,无意中看见一片博文关于配置vsftp虚拟用户配置 它的vsftp目录结构和我这台服务器上的一样,包括好多文件像txt.db 这才发现原来使用的这个方

iOS7滑动返回

本文转载至 http://www.cnblogs.com/lexingyu/p/3432444.html iOS 7中在传统的左上角返回键之外,提供了右滑返回上一级界面的手势.支持此手势的是UINavigationController中新增的属性 interactivePopGestureRecognizer,即右滑返回只支持以UINavigationController为容器的ViewController间切换,要想在自定义容器中使用,需要一些额外的工作. 基本地,控制ViewControll

navigation 定义leftBarButtonItem 无法滑动返回的解决方案

iOS 的导航栏定义leftBarButtonItem 默认的滑动返回 会失效 [_currentNav pushViewController:viewController animated:YES]; //开启iOS7的滑动返回效果 if ([_currentNav respondsToSelector:@selector(interactivePopGestureRecognizer)]) { _currentNav.interactivePopGestureRecognizer.deleg

Android-通过SlidingMenu高仿微信6.2最新版手势滑动返回(二)

转载请标明出处: http://blog.csdn.net/hanhailong726188/article/details/46453627 本文出自:[海龙的博客] 一.概述 在上一篇博文中,博文地址Android-通过SlidingPaneLayout高仿微信6.2最新版手势滑动返回(一),我们是通过官方自带的SlidingPaneLayout来实现的手势滑动返回.在这篇博文中,我们将採用SlidingMenu来高仿. 事实上实现的原理都一样.仅仅只是是把SlidingPaneLayout

JQ 移动端返回顶部,往下滑动时显示返回按钮,往上滑动时隐藏返回按钮

returnTop:function(){ //预定义返回顶部的html代码,它的css样式默认为不显示 var gotoTop_html = '<div class="returnTop"></div>'; //将返回顶部的html代码插入页面上id为page的元素的末尾 $(".ding_C_returnTop").append(gotoTop_html); var windowTop=0;//初始话可视区域距离页面顶端的距离 $(doc

ios7去除手势滑动返回

if ([self.navigationController respondsToSelector:@selector(interactivePopGestureRecognizer)]) { self.navigationController.interactivePopGestureRecognizer.enabled = NO; } ios7去除手势滑动返回

一起学android之解决在scrollview中嵌套ListView切换界面时scrollview整体向下滑动(31)

有时候我们在scrollview中会嵌套ListView,在嵌套ListView(ListView上面还有一些控件)后,切换Activity时会发现整个 scrollview 想下滑动直到ListView置顶了,而上面的控件无法显示时,我们可以这样做: 将图中的View放在顶部,最后在代码中这样做: view.setFocusable(true); view.setFocusableInTouchMode(true); view.requestFocus(); 通过setFocusable和se

MySQL学习笔记_12_Linux下C++/C连接MySQL数据库(二) --返回数据的SQL

 Linux下C++/C连接MySQL数据库(二) --返回数据的SQL 引: 返回数据的SQL是指通过查询语句从数据库中取出满足条件的数据记录 从MySQL数据库值哦功能检索数据有4个步骤: 1)发出查询 2)检索数据 3)处理数据 4)整理所需要的数据 用mysql_query()发出查询,检索数据可以使用mysql_store_result()或mysql_use_result(),取决与怎样检索数据,接着是调用mysql_fetch_row()来处理数据,最后,还必须调用mysql_