索引
- 引言
-
Pop Gesture Recognizer Bugs
2.1 Pop Gesture Recoginzer 失效
2.2 Pop Gesture Recognizer 导致 App 假死现象
2.3 Pop Gesture Recognizer 取消 Pop 后 导航栏 错乱问题 -
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 假死现象
先不要开心的太早, 返回手势虽然回来了, 但是这导致了一个新的问题, 当你在 NavigationController
的 RootViewController
中尝试做一次边缘返回手势
试一下. 可能有人会说:"没反应啊, 一点反应没有", 没错, 确实没有反应, 但是我可以很负责任的告诉你, 你的 App 现在已经出问题了, 而且是一个很严重的问题. 不信? 随便点击一个按钮, 尝试 Push
到下一个ViewController
试试! 说这么多, 其实也没有把图片放上来来的实在. 先来看一张正常的 App 结构图:
当我触发了这个 Bug 之后, 再来看一下结构
下面这张图, 是触发 Bug 后, App 的状态, 页面已经卡死在这个部分了. (按钮的高亮状态, 并不是截图效果, 而是 App 真的已经卡死在这个状态上了)
为什么说是假死呢? 因为实际上这个 App 根本没有卡死, 上一张动图给大家看看:
看完动图有没有觉得很诡异, 明明已经卡死了, 可是居然可以在首页
中使用 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
操作. 主要的实现逻辑其实就几步:
- 自定义一个
NavigationController
继承自UINavigationController
. - 使用
FOUNDATION_EXPORT SEL NSSelectorFromString(NSString *aSelectorName);
方法获取 Apple 工程师开发的Pop
方法. - 创建一个
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
跑一下程序试试看, 其实效果还是蛮不错的. 但是有两个问题存在:
- 这是老问题了, 如果不限制手势的出发条件, App 依然会出现假死的情况.
- 这个是新的问题: 当我们
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 在简书这个平台能够更快速的成长
上一篇: 导航栏隐藏 && 导航栏错乱