一行代碼,讓你的應(yīng)用中UIScrollView的滑動(dòng)與側(cè)滑返回并存

側(cè)滑返回是iOS系統(tǒng)的一個(gè)很貼心的功能,特別是在大屏手機(jī)上,單手操作的時(shí)候去按左上角的返回鍵特別不方便。當(dāng)我在使用一個(gè)APP的時(shí)候,如果控制器不能側(cè)滑返回,我會(huì)覺得這個(gè)APP十分不友好...這款產(chǎn)品在我心中的印象分也會(huì)有所降低...
側(cè)滑返回本身是系統(tǒng)自帶的效果,無須開發(fā)者處理的。但是,一旦給控制器加了leftBarButtonItem,系統(tǒng)側(cè)滑手勢(shì)居然失效了(蘋果你這樣真的好嗎...),要重新支持側(cè)滑返回,可以做以下處理:

一、讓應(yīng)用支持側(cè)滑返回

  1. 導(dǎo)入FDFullscreenPopGesture庫(kù),pod安裝一下即可,安裝后無須再加任何代碼,應(yīng)用所有頁(yè)面都將支持全屏側(cè)滑。github地址:https://github.com/forkingdog/FDFullscreenPopGesture
    如果你或者你們產(chǎn)品不喜歡全屏側(cè)滑,還是喜歡在屏幕左邊緣處才觸發(fā)側(cè)滑,那可以看看第二種方式:
  2. 寫一個(gè)UINavigationController的子類,設(shè)置導(dǎo)航控制器的代理為自己,實(shí)現(xiàn)下面這個(gè)代理方法:
- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated {
    // 讓系統(tǒng)的側(cè)滑返回生效
    self.interactivePopGestureRecognizer.enabled = YES;
    if (viewController == self.viewControllers[0]) {
       self.interactivePopGestureRecognizer.delegate = self.tz_PopDelegate; // 不支持側(cè)滑
    } else {
        self.interactivePopGestureRecognizer.delegate = nil; // 支持側(cè)滑
    }
 }

viewDidLoad方法中,給popDelegate賦值: self.popDelegate = self.interactivePopGestureRecognizer.delegate 。這樣操作后,即使給控制器加了leftBarButtonItem,系統(tǒng)的側(cè)滑返回也能生效了。當(dāng)然,我所使用的絕大部分APP,都能有側(cè)滑返回的效果,想必上面的內(nèi)容大家都是掌握的~ 不過有一款A(yù)PP,我想要指出一下:《自如》...你為什么不支持側(cè)滑返回...你對(duì)得起你們的60萬自如客嗎...

二、怎么讓UIScrollView和側(cè)滑返回并存?

對(duì)側(cè)滑返回的基礎(chǔ)介紹完畢,接下來是本文的重點(diǎn)咯:
大家在開發(fā)中一定遇到過這個(gè)問題,如果界面中有一個(gè)可以水平滑動(dòng)的UIScrollView,那么系統(tǒng)側(cè)滑返回又失效了...因?yàn)槟阍谄聊蛔筮吘墏?cè)滑的時(shí)候,做出響應(yīng)的是UIScrollView,事件并沒有傳遞到UINavigationController的view,所以也就沒有了側(cè)滑返回。這種情況該怎么處理呢?我們先來看看別的應(yīng)用是怎么處理的:
阿里系:《淘寶》、《支付寶》、《菜鳥裹裹》。我手機(jī)上裝的三個(gè)阿里系A(chǔ)PP都沒有處理這種情況,在其地圖頁(yè)面?zhèn)然祷鼐Я?..
新美大:很遺憾,《美團(tuán)》和《大眾點(diǎn)評(píng)》也陣亡了...
騰訊系:《QQ》陣亡,而《微信》...成為我發(fā)現(xiàn)的第一個(gè)對(duì)這種情況做出很好處理的應(yīng)用。在微信訂閱號(hào)圖文閱讀界面的圖片瀏覽器、聊天界面查看共享位置的地圖界面,均支持側(cè)滑返回,又不影響界面本身的滑動(dòng),給微信點(diǎn)贊~
此外,在寫這篇博文的時(shí)候,我還測(cè)試了幾個(gè)APP,其中《鏈家》和《鏈家上海》也對(duì)這種情況做了很好的處理,點(diǎn)贊~
比較奇怪的是,鏈家上海和鏈家的圖片選擇器用的不是同一個(gè),鏈家上海在聊天頁(yè)面用的圖片選擇器是我寫的TZImagePickerControllergithub鏈接:https://github.com/banchichen/TZImagePickerController,而鏈家不是...如果大家兩個(gè)應(yīng)用都裝了的話,可以明顯感受到鏈家的圖片選擇器體驗(yàn)明顯不如TZImagePickerController,比如圖片顯示模糊,在快速滑動(dòng)時(shí)有明顯的卡頓感等,嘎嘎,理性地打個(gè)廣告...詳細(xì)介紹請(qǐng)見上篇博文~

好了,那微信和鏈家是怎么讓UIScrollView的滑動(dòng)與系統(tǒng)側(cè)滑并存的呢?
首先我們打開微信,隨機(jī)點(diǎn)開一個(gè)聊天窗口發(fā)送位置給對(duì)方,點(diǎn)擊位置信息查看地圖,我們發(fā)現(xiàn)當(dāng)在屏幕左邊緣向左滑動(dòng)時(shí),觸發(fā)的是側(cè)滑返回,其他情況都觸發(fā)的是地圖的滑動(dòng)。
我們知道,側(cè)滑返回效果的觸發(fā)需要滿足:(1) 滑動(dòng)位置在屏幕左邊緣;(2)向右滑動(dòng);
在微信查看地圖界面,我們發(fā)現(xiàn)當(dāng)側(cè)滑返回手勢(shì)生效時(shí),地圖滑動(dòng)則失效。只有在側(cè)滑返回失效時(shí),地圖才會(huì)滑動(dòng)。達(dá)到了兩個(gè)手勢(shì)并存的效果。

三、集成TZScrollViewPopGesture,一行代碼搞定

基于以上的思考,在參考了FDFullscreenPopGesture和數(shù)篇博客后,我的解決思路如下:
1、(1) 創(chuàng)建一個(gè)UIPanGestureRecognizer手勢(shì),該手勢(shì)觸發(fā)時(shí)執(zhí)行側(cè)滑返回的action,為了使用方便,我將其寫在分類里面,代碼如下:

- (UIPanGestureRecognizer *)tz_popGestureRecognizer {
    UIPanGestureRecognizer *pan = objc_getAssociatedObject(self, _cmd);
    if (!pan) {
        // 側(cè)滑返回手勢(shì) 手勢(shì)觸發(fā)的時(shí)候,讓target執(zhí)行action
        id target = self.tz_PopDelegate;
        SEL action = NSSelectorFromString(@"handleNavigationTransition:");
        pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:action];
        pan.maximumNumberOfTouches = 1;
        pan.delegate = self;
        self.interactivePopGestureRecognizer.enabled = NO;
        objc_setAssociatedObject(self, _cmd, pan, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return pan;
 }

(2) 模仿一下系統(tǒng)側(cè)滑返回手勢(shì)的觸發(fā)條件,給這個(gè)手勢(shì)限定合適的觸發(fā)條件(滑動(dòng)位置在屏幕左邊緣+向右滑),代碼如下:

- (BOOL)gestureRecognizerShouldBegin:(UIPanGestureRecognizer *)gestureRecognizer {
     if ([[self valueForKey:@"_isTransitioning"] boolValue]) {
         return NO;
     }
     if (self.childViewControllers.count <= 1) {
         return NO;
     }
     // 側(cè)滑手勢(shì)觸發(fā)位置
     CGPoint location = [gestureRecognizer locationInView:self.view];
     CGPoint offSet = [gestureRecognizer translationInView:gestureRecognizer.view];
     BOOL ret = (0 < offSet.x && location.x <= 40);
     NSLog(@"%@ %@",NSStringFromCGPoint(location),NSStringFromCGPoint(offSet));
     return ret;
}

(3) 側(cè)滑手勢(shì)優(yōu)先,側(cè)滑手勢(shì)失效時(shí),才觸發(fā)UISrcollView的滑動(dòng)

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldBeRequiredToFailByGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
     return YES;
}

2、手勢(shì)已經(jīng)定義好了,接下來把它加在合適的UISrcollView上面即可,我于是給UIViewController寫了個(gè)分類,新增方法:- (void)tz_addPopGestureToView:(UIView *)view; 在需要UISrcollView滑動(dòng)和系統(tǒng)側(cè)滑同時(shí)并存的界面中,只需要在控制器中調(diào)用這個(gè)方法即可解決:[self tz_addPopGestureToView:scrollView];

我在我的多個(gè)項(xiàng)目中測(cè)試這個(gè)分類,發(fā)現(xiàn)效果都十分理想,和微信、鏈家APP的效果是一致的。使用起來還算方便,一行代碼即可搞定。經(jīng)過測(cè)試,TZScrollViewPopGestureFDFullscreenPopGesture并不會(huì)沖突,兩者可以和諧共存,大家不妨一試。下面放個(gè)截個(gè)圖讓大家看看效果~

以上代碼均可在Demo中見到,更多信息詳見Demo,github地址:https://github.com/banchichen/TZScrollViewPopGesture
當(dāng)然,TZScrollViewPopGesture也支持pod安裝,在podfile中加入 pod 'TZScrollViewPopGesture' 即可。
老規(guī)矩,大家覺得好的話,給一個(gè)小小的star鼓勵(lì)一下噢~

技術(shù)進(jìn)步一小步,用戶體驗(yàn)也進(jìn)步一小步...有進(jìn)步就好~
(順便吐槽一下支付寶等諸多應(yīng)用對(duì)鍵盤類型的不良處理,明明是讓我輸入數(shù)字驗(yàn)證碼,你不給我彈出數(shù)字鍵盤真的好嗎.......)

參考鏈接:
輕松學(xué)習(xí)之二——iOS利用Runtime自定義控制器POP手勢(shì)動(dòng)畫
【8行代碼教你搞定導(dǎo)航控制器全屏滑動(dòng)返回效果】 |那些人追的干貨
參考代碼:
FDFullscreenPopGesture

今天已經(jīng)10.05了,轉(zhuǎn)眼國(guó)慶變成了中秋...
再過一天,中秋就變成了周末...提前祝大家周末快樂~!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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