iOS側滑返回和UIScrollView手勢衝突如何解決

最近在開發中遇到這樣一個問題,有一個UICollectionView採用水平方向滑動方式,當往右滑動時,常常與側滑返回的手勢出現衝突,致使原本想滑到左邊,結果給直接Pop到上一個頁面了,本篇就是尋找解決這個問題的方法。bash

實驗一app

首先想到了就是採用手勢衝突的常規方法,代碼以下:ide

if let gesture = self.navigationController?.interactivePopGestureRecognizer {
    gesture.require(toFail: self.collectionView.panGestureRecognizer)
}
複製代碼

實驗結果代表,這個方法的效果等因而禁用了側滑返回的手勢,由於水平方向UIScrollView的panGestureRecognizer不會失敗,可能有人立馬想到bounces屬性,添加上self.collectionView.bounces = false這一句試試效果,結果證實,此方法仍是行不通。工具

實驗二ui

上面簡單的方法不行,那就要從攔截手勢事件着手了,能夠從UIScollView的panGesture或者側滑手勢兩個方向嘗試,我我的比較傾向於從側滑手勢方向,因此接下來就記錄下這個方向的實驗過程。spa

在WXViewController中設置代理,代碼以下:代理

self.navigationController?.interactivePopGestureRecognizer?.delegate = self
if let gesture = self.navigationController?.interactivePopGestureRecognizer {
    self.collectionView.panGestureRecognizer.require(toFail: gesture)
}
複製代碼

實現UIGestureDelegate協議調試

extension WXViewController: UIGestureRecognizerDelegate {
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if self.collectionView.contentOffset.x <= 0 {
            return true
        }
        return false
    }
}
複製代碼

實驗證實,上面的方法是可行的,可是帶來了一個反作用,就是其餘頁面的側滑手勢失效了,顯然是因爲側滑的手勢被WXViewController攔截了,其餘頁面的側滑手勢的delegate爲nil,致使系統不能響應了。 解決的辦法是,要保存 interactivePopGestureRecognizer?.delegate的代理,而後在適當的地方再還原,在上面的代碼中添加以下代碼:code

popGestrueDelegate = self.navigationController?.interactivePopGestureRecognizer?.delegate
self.navigationController?.interactivePopGestureRecognizer?.delegate = self
......

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
 self.navigationController?.interactivePopGestureRecognizer?.delegate = popGestrueDelegate
}
複製代碼

實驗結果代表,上面的方法解決了側滑和水平方向滑動的手勢衝突問題,可是看看代碼感受還不是特別完美,若是其餘頁面也須要解決這個手勢衝突呢,那就須要把這些代碼copy過去了,因而繼續試驗。cdn

試驗三

既然interactivePopGestureRecognizer是定義在UINavigationController中,那咱們也能夠自定義一個UINavigationController,而後攔截這個手勢了,代碼以下

/*自定義攔截手勢協議*/
protocol WXInteractiveGestureDelegate: NSObjectProtocol  {
    func wxGestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool
}
/*WXNavigationController*/
override func viewDidLoad() {
    super.viewDidLoad()
    self.interactivePopGestureRecognizer?.delegate = self
}

extension WXNavigationController: UIGestureRecognizerDelegate {
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if let topController = self.topViewController as? WXInteractiveGestureDelegate {
            return topController.wxGestureRecognizerShouldBegin(gestureRecognizer)
        }
        return true
    }
}
複製代碼

而後在須要攔截側滑手勢的UIViewController中實現該協議,

extension WXViewController: WXInteractiveGestureDelegate {
    func wxGestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if self.collectionView.contentOffset.x <= 0 {
            return true
        }
        return false
    }
}
複製代碼

經過上面方法,實驗發現並無解決問題,是哪裏的問題呢,打斷點調試發現,代理方法是有調用,也是返回true的,可是最後接受手勢的確實UIScrollView的panGestrue,因此仍是須要添加如下邏輯

if let gesture = self.navigationController?.interactivePopGestureRecognizer {
    self.collectionView.panGestureRecognizer.require(toFail: gesture)
}
複製代碼

添加以後看看效果,嗯,效果還不錯。

側滑手勢.gif

後記

開始時說過還能夠從UIScrollView的手勢着手,有興趣的同窗能夠去嘗試一下,若是有其餘的更好方法,也歡迎留言討論。

如需轉載,請告知做者並註明做者和來源,謝謝!

注:

一、gif製做工具,PicGIF

相關文章
相關標籤/搜索