情景再現(xiàn)
如下圖所示,仿微信聊天界面的錄音按鈕止后,放在屏幕的最下方
問題一:每次按鈕按下時(shí)都有1秒左右的延遲,體驗(yàn)很差溜腐。于是按照網(wǎng)上所說的,將屏幕底部的系統(tǒng)手勢(shì)屏蔽掉瓜喇;
問題二:即便按照上述方法屏蔽掉了系統(tǒng)手勢(shì)挺益,按鈕的左半部分依然存在延遲。于是繼續(xù)按照網(wǎng)上所說的乘寒,將導(dǎo)航的右滑返回屏蔽掉望众。
如此的解決方案,大概沒有人會(huì)采用伞辛。
難道真如網(wǎng)上所說的烂翰,二者不可兼得嗎?
但是為何微信卻做到了蚤氏?
難道是我不如馬化騰嗎甘耿?
作為一個(gè)站在食物鏈頂端的男人,我不服竿滨!
帶著這種不屈不撓的精神佳恬,我開始了探索...
(探索過程略)
終于,我成功了于游!
下面是完整的處理代碼毁葱,下文會(huì)結(jié)合代碼一一講解
#import "LLChatViewController.h"
//底部view的高
#define TOOL_HEIGHT 49
//屏幕高
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
@interface LLChatViewController ()<UIGestureRecognizerDelegate>
//記錄當(dāng)前導(dǎo)航的手勢(shì)代理
@property (nonatomic, weak) id<UIGestureRecognizerDelegate> recognizerDelegate;
@property (nonatomic, assign, getter=isDeferredSystemGestures) BOOL deferredSystemGestures;
@end
@implementation LLChatViewController
- (void)viewDidLoad {
[super viewDidLoad];
//首先解決屏幕底部的系統(tǒng)手勢(shì)引起的延遲
//屏蔽系統(tǒng)底部手勢(shì)
self.deferredSystemGestures = YES;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self updateRecognizerDelegate:YES];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self updateRecognizerDelegate:NO];
}
#pragma mark - 錄音按鈕手勢(shì)沖突處理
//設(shè)置手勢(shì)代理
- (void)updateRecognizerDelegate:(BOOL)appear {
if (appear) {
if (self.recognizerDelegate == nil) {
self.recognizerDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
}
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
else {
self.navigationController.interactivePopGestureRecognizer.delegate = self.recognizerDelegate;
}
}
//是否響應(yīng)觸摸事件
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (self.navigationController.viewControllers.count <= 1) return NO;
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint point = [touch locationInView:gestureRecognizer.view];
//判斷是否觸摸在底部view上, 是則不響應(yīng)導(dǎo)航右滑返回事件
if (point.y > SCREEN_HEIGHT-TOOL_HEIGHT) {
return NO;
}
if (point.x <= 100) {//設(shè)置手勢(shì)觸發(fā)區(qū)
return YES;
}
}
return NO;
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer {
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGFloat tx = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:gestureRecognizer.view].x;
if (tx < 0) {
return NO;
}
}
return YES;
}
//是否與其他手勢(shì)共存,一般使用默認(rèn)值(默認(rèn)返回NO:不與任何手勢(shì)共存)
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer{
//UIScrollView的滑動(dòng)沖突
if ([otherGestureRecognizer.view isKindOfClass:[UIScrollView class]]) {
UIScrollView *scrollow = (UIScrollView *)otherGestureRecognizer.view;
if (scrollow.bounds.size.width >= scrollow.contentSize.width) {
return NO;
}
if (scrollow.contentOffset.x == 0) {
return YES;
}
}
return NO;
}
//屏蔽屏幕底部的系統(tǒng)手勢(shì)
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
if (self.isDeferredSystemGestures) {
return UIRectEdgeBottom;
}
return UIRectEdgeNone;
}
@end
細(xì)心地同學(xué)可能會(huì)發(fā)現(xiàn)贰剥, deferredSystemGestures
這個(gè)屬性我就在viewDidLoad
中賦值了一次倾剿,自始至終都是YES。
那么你們肯定有疑問蚌成,既然如此前痘,下面這段代碼:
//屏蔽屏幕底部的系統(tǒng)手勢(shì)
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
if (self.isDeferredSystemGestures) {
return UIRectEdgeBottom;
}
return UIRectEdgeNone;
}
是不是可以寫成這樣:
//屏蔽屏幕底部的系統(tǒng)手勢(shì)
- (UIRectEdge)preferredScreenEdgesDeferringSystemGestures {
return UIRectEdgeBottom;
}
答案是不可以,并且deferredSystemGestures
賦值這段代碼笑陈,只能放在viewDidLoad
被調(diào)用之后际度。假如將deferredSystemGestures
賦值寫在viewDidLoad
被調(diào)用之前,比如說放在初始化init
里面涵妥,這兩段代碼就會(huì)等價(jià)乖菱。
下面我們來詳細(xì)講解
preferredScreenEdgesDeferringSystemGestures
方法的調(diào)用時(shí)機(jī):
1坡锡、當(dāng)viewDidLoad
被調(diào)用之前,系統(tǒng)會(huì)自動(dòng)調(diào)用preferredScreenEdgesDeferringSystemGestures
方法窒所,以確定是否延遲處理屏幕邊緣的系統(tǒng)手勢(shì)鹉勒。此時(shí),self.isDeferredSystemGestures
的值為false
吵取,方法返回結(jié)果為UIRectEdgeNone
禽额,不延遲任何邊緣手勢(shì),才使得屏幕底部上滑事件成為可能皮官;
2脯倒、當(dāng)視圖加載完成后,我們點(diǎn)擊錄音按鈕的時(shí)候捺氢,由于按鈕位置靠近屏幕邊緣藻丢,造成手勢(shì)沖突,系統(tǒng)會(huì)調(diào)用preferredScreenEdgesDeferringSystemGestures
方法來確定優(yōu)先處理哪個(gè)手勢(shì)摄乒。此時(shí)悠反,self.isDeferredSystemGestures
的值為true
,方法返回結(jié)果為UIRectEdgeBottom
馍佑,延遲處理屏幕底部手勢(shì)斋否,才使得錄音按鈕能第一時(shí)間響應(yīng)成為可能;
3拭荤、當(dāng)我們直接從手機(jī)邊緣上滑時(shí)茵臭,無手勢(shì)沖突,因此穷劈,不會(huì)調(diào)用preferredScreenEdgesDeferringSystemGestures
笼恰,并且視圖加載前系統(tǒng)已確認(rèn)不延遲任何邊緣手勢(shì),因此可直接處理上滑事件歇终。
至此社证,由屏幕底部的系統(tǒng)手勢(shì)引起的延遲已完美解決。
接下來就是問題二评凝,導(dǎo)航右滑返回引起的按鈕左半部分的延遲追葡。
解決方法也不難,哪里有沖突就從哪里解決奕短。既然是右滑返回引起的宜肉,那我們首先拿到右滑手勢(shì)。
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self updateRecognizerDelegate:YES];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self updateRecognizerDelegate:NO];
}
//設(shè)置手勢(shì)代理
- (void)updateRecognizerDelegate:(BOOL)appear {
if (appear) {
if (self.recognizerDelegate == nil) {
self.recognizerDelegate = self.navigationController.interactivePopGestureRecognizer.delegate;
}
self.navigationController.interactivePopGestureRecognizer.delegate = self;
}
else {
self.navigationController.interactivePopGestureRecognizer.delegate = self.recognizerDelegate;
}
}
這段代碼的大概意思就是翎碑,當(dāng)視圖出現(xiàn)時(shí)谬返,將手勢(shì)代理設(shè)置為self
,當(dāng)時(shí)圖消失時(shí)日杈,再還原成原來的值遣铝。
接下來就是手勢(shì)的處理
//是否響應(yīng)觸摸事件
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
if (self.navigationController.viewControllers.count <= 1) return NO;
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]]) {
CGPoint point = [touch locationInView:gestureRecognizer.view];
//判斷是否觸摸在底部view上, 是則不響應(yīng)導(dǎo)航右滑返回事件
if (point.y > SCREEN_HEIGHT-TOOL_HEIGHT) {
return NO;
}
if (point.x <= 100) {//設(shè)置手勢(shì)觸發(fā)區(qū)
return YES;
}
}
return NO;
}
當(dāng)我們觸摸到這個(gè)位置point.y > SCREEN_HEIGHT-TOOL_HEIGHT
佑刷,也就是底部整個(gè)錄音視圖的位置時(shí),返回false
酿炸,不響應(yīng)滑動(dòng)返回瘫絮,使得錄音按鈕能第一響應(yīng)成為可能。
見證奇跡的時(shí)刻
需要看效果的同學(xué)可以去下載我寫的這個(gè)聊天框架填硕,試一試私聊界面的錄音按鈕麦萤。
一個(gè)完整的聊天UI框架 - 點(diǎn)我下載