【完美解決篇】iOS11下底部手勢(shì)延遲引起的問題

情景再現(xiàn)

如下圖所示,仿微信聊天界面的錄音按鈕止后,放在屏幕的最下方

示例圖.png

問題一:每次按鈕按下時(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)我下載

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市扁眯,隨后出現(xiàn)的幾起案子壮莹,更是在濱河造成了極大的恐慌,老刑警劉巖姻檀,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件垛孔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡施敢,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門狭莱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來僵娃,“玉大人,你說我怎么就攤上這事腋妙∧梗” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵骤素,是天一觀的道長(zhǎng)匙睹。 經(jīng)常有香客問我,道長(zhǎng)济竹,這世上最難降的妖魔是什么痕檬? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮送浊,結(jié)果婚禮上梦谜,老公的妹妹穿的比我還像新娘。我一直安慰自己袭景,他們只是感情好唁桩,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著耸棒,像睡著了一般荒澡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上与殃,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天单山,我揣著相機(jī)與錄音碍现,去河邊找鬼。 笑死饥侵,一個(gè)胖子當(dāng)著我的面吹牛鸵赫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播躏升,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼辩棒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了膨疏?” 一聲冷哼從身側(cè)響起一睁,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎佃却,沒想到半個(gè)月后者吁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饲帅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年复凳,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片灶泵。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡育八,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赦邻,到底是詐尸還是另有隱情髓棋,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布惶洲,位于F島的核電站按声,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏恬吕。R本人自食惡果不足惜签则,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望币呵。 院中可真熱鬧怀愧,春花似錦、人聲如沸余赢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)妻柒。三九已至扛拨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間举塔,已是汗流浹背绑警。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工求泰, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人计盒。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓渴频,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親北启。 傳聞我的和親對(duì)象是個(gè)殘疾皇子卜朗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容