PopGestureRecognizer Tips

索引

  1. 引言
  2. Pop Gesture Recognizer Bugs
    2.1 Pop Gesture Recoginzer 失效
    2.2 Pop Gesture Recognizer 导致 App 假死现象
    2.3 Pop Gesture Recognizer 取消 Pop 后 导航栏 错乱问题
  3. Fullscreen Pop Gesture Recognizer
    3.1 Fullscreen Pop Gesture Recognizer 的简单实现
    3.2 Fullscreen Pop Gesture Recognizer 控制是否允许触发手势

引言


近些日子一直在忙于公司项目的开发, 也鲜有时间对自己的技术 和 遇到的困难做一个总结, 今天抽时间把遇到的疑难杂症总结一下. 也是希望大家看到这篇文章之后可以少入点坑. 话说这从 iOS7 苹果添加了边缘返回手势之后, 还真的是带来了不少的疑难杂症. 不细心的同学还真的挺难发现问题原因的. 从导航栏错乱, 到 App 假死, 再到全屏返回手势的实现, 确实也是在 Google 翻阅了大量的资料, 甭管是中文的还是英文的, 看得懂的还是看不懂的, 反正是一大堆, 才对此类问题稍微有了一番了解. 接下来就开始进入正题了, 先从 边缘返回手势带来的 Bug 说起吧.

Pop Gesture Recognizer Bugs


Pop Gesture Recognizer 失效

边缘返回手势失效, 这是一个非常常见的问题了, 不可否认的一点是, 可以说只要是打算做 App 的公司, 他都会自定义 NavigationBar 的返回按钮. 当 Developer 自定义了一个返回按钮并兴高采烈的 Command + R 运行程序后, 会发现一个让人伤心的问题, 边缘返回手势没有了. 还有另外一中情况, 就是当你有一个 ViewController 设置了 NavigationBar 隐藏, 那么边缘返回手势也会失效. 其实解决这个问题的方法很简单, 一句代码搞定, 代码如下:


#pragma mark ViewWillAppear
// 在 BaseViewController 的 ViewWillAppear 中, 设置返回手势的代理为 Self 即可.
- (void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear: animated];
    
    // 1. 返回手势代理
    self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
}

有了这句代码, 赶紧 Command + R 再次运行程序, 怎么样, 返回手势是不是回来了? Mabey That's Great!

Pop Gesture Recognizer 导致 App 假死现象

先不要开心的太早, 返回手势虽然回来了, 但是这导致了一个新的问题, 当你在 NavigationControllerRootViewController 中尝试做一次边缘返回手势试一下. 可能有人会说:"没反应啊, 一点反应没有", 没错, 确实没有反应, 但是我可以很负责任的告诉你, 你的 App 现在已经出问题了, 而且是一个很严重的问题. 不信? 随便点击一个按钮, 尝试 Push 到下一个ViewController 试试! 说这么多, 其实也没有把图片放上来来的实在. 先来看一张正常的 App 结构图:

这是一个正常的 App 首页结构

当我触发了这个 Bug 之后, 再来看一下结构


触发 Bug 后, App 的结构

下面这张图, 是触发 Bug 后, App 的状态, 页面已经卡死在这个部分了. (按钮的高亮状态, 并不是截图效果, 而是 App 真的已经卡死在这个状态上了)

触发 Bug 后, App 的状态

为什么说是假死呢? 因为实际上这个 App 根本没有卡死, 上一张动图给大家看看:

123123.gif

看完动图有没有觉得很诡异, 明明已经卡死了, 可是居然可以在首页中使用 Pop 手势! 导致这个问题产生的原因, 我猜测应该是因为我们刚才把Pop Gesture的代理设置成了我们自己的ViewController, 这必然就产生了一潜在的问题. 在我们设置代理之前, 为什么不会产生这个问题? 为什么设置代理之后却产生了这么严重的问题呢? 如果猜想无误的话, 肯定是 Apple 工程师在 Gesture Delegate 方法中做了某些限制, 那我们尝试的去做一下. 看能否解决这个问题. 查看UIGestureRecognizer.h中的代理方法, 看到一个比较心仪的方法: - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;, 我们尝试着去用一下这个方法, 在设置代理的 ViewController 中, 实现这个代理方法, 代码如下:

#pargma mark - 方法1: 在手势的代理方法中, 判断是否允许启动手势
#Pargma mark -
#pargma mark 是否允许手势启动
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {

    // 如果 Self 是 NaivationController 的 根试图控制器, 则不允许启动手势
    if (self == [self.navigationController.viewControllers firstObject]) {
        return NO;
    }
    
    return YES;
}


#pargma mark - 方法2: 在 View Did Appear 中, 开启或关闭边缘返回手势
#pargma mark -
#pargma mark View Did Appear
- (void) viewDidAppear:(BOOL)animated {
  [super viewDidAppear: animated];
  
  // 如果 Self 是 NavigationController 的 根视图控制器, 则关闭返回手势.
  if (self == [self.navigationController.viewControllers firstObject]) {
      self.navigationController.interactivePopGestureRecognizer.enabled = NO;
  } else {
      self.navigationController.interactivePopGestureRecognizer.enabled = YES;
  }
}

Command + R 把程序跑起来, 再来试试, OK, 问题解决了, 说明我们的猜测是正确的. 当我们把手势的代理指向了我们自己的类, 那 Apple 实现的一下限制手势的逻辑就失效了, 也就导致了这个 Bug 的产生.

Pop Gesture Recognizer 取消 Pop 后 导航栏 错乱问题

这个问题在我的另一篇文章导航栏隐藏 && 导航栏错乱中写得很详细了, 在这里就不做过多的赘述了.

Fullscreen Pop Gesture Recognizer


Fullscreen Pop Gesture Recognizer 的简单实现

随着手机屏幕尺寸的逐渐增大, 仅仅依靠左上角的返回按钮 和 左侧边缘出发的返回手势, 已经很难满足用户的需求了. 我们也可以看到, 越来越多的主流 App 中都已经加入了全屏的 Pop 手势了, 随便举几个例子:简书,今日头条,网易新闻等, 例子不胜枚举, 在这里我就不说那么多了. 在这里跟大家分享一个简单的实现方法, 为什么说是简单的实现方法呢? 因为确实是用了一个投机取巧的方法. 那就是: 使用 Apple 工程师为我们写好的方法进行 Pop 操作. 主要的实现逻辑其实就几步:

  1. 自定义一个 NavigationController 继承自 UINavigationController.
  2. 使用 FOUNDATION_EXPORT SEL NSSelectorFromString(NSString *aSelectorName); 方法获取 Apple 工程师开发的 Pop 方法.
  3. 创建一个 UIPanGestureRecognizer, 并将手势添加到 NavigationController.view中.
    就这简单的 3 个步骤, 就完成了全屏返回手势的实现, 核心代码如下:
@interface MLNavigationController () <UIGestureRecognizerDelegate>

@property (nonatomic, strong) UIPanGestureRecognizer *popPanGesture;

@property (nonatomic, strong) id popGestureDelegate;

@end

@implementation MLNavigationController
#pragma mark - ViewController Life Circle
#pragma mark -
#pragma mark ViewDidLoad
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 1. Fullscreen Pop Gesture
    [self addFullScreenPopGestureAction];
}

#pragma mark - Private Methods
#pragma mark -
#pragma mark Add Fullscreen Pop Gesture
- (void)addFullScreenPopGestureAction
{
    self.popGestureDelegate = self.interactivePopGestureRecognizer.delegate;
    SEL action = NSSelectorFromString(@"handleNavigationTransition:");
    self.popPanGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self.popGestureDelegate action:action];
    self.popPanGesture.maximumNumberOfTouches = 1;
    self.popPanGesture.delegate = self;
    [self.view addGestureRecognizer: self.popPanGesture];
}

@end

Fullscreen Pop Gesture Recognizer 控制是否允许触发手势

用这种方法, 真的非常简单的就实现了全屏返回手势, Command + R跑一下程序试试看, 其实效果还是蛮不错的. 但是有两个问题存在:

  1. 这是老问题了, 如果不限制手势的出发条件, App 依然会出现假死的情况.
  2. 这个是新的问题: 当我们 Push 出来一个ViewController 并且尝试用全屏手势返回的时候, 我们会发现, 无论你是左划, 还是右划, App 都会响应. 只不过当你手指从右往左划的时候, 状态栏会消失. 仔细观察一下.

解决这两个问题的思路, 和之前一样, 实现手势的代理方法 - (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer;, 在这里控制手势是否允许执行. 代码如下:

#pragma mark - UIGesture Delegate
#pragma mark -
#pragma mark Should Begin
- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer
{
    // Prevent Pan Gesture From Right To Left
    CGPoint translation = [gestureRecognizer translationInView:gestureRecognizer.view];
    if (translation.x <= 0) return NO;
    
    // Root View Controller Can Not Begin The Pop Gesture
    if (self.viewControllers.count <= 1) return NO;
    
    return YES;
}

在代理方法中, 添加两个条件来控制手势是否允许执行. 再跑一下程序试试看, 问题解决了. 有没有很爽的感觉?


Lemon龙说:

如果您在文章中看到了错误 或 误导大家的地方, 请您帮我指出, 我会尽快更改

如果您有什么疑问或者不懂的地方, 请留言给我, 我会尽快回复您

如果您觉得本文对您有所帮助, 您的喜欢是对我最大的鼓励

如果您有好的文章, 可以投稿给我, 让更多的 iOS Developer 在简书这个平台能够更快速的成长


上一篇: 导航栏隐藏 && 导航栏错乱

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

推荐阅读更多精彩内容