iOS侧滑返回和UIScrollView手势冲突如何解决

最近在开发中遇到这样一个问题,有一个UICollectionView采用水平方向滑动方式,当往右滑动时,经常与侧滑返回的手势出现冲突,导致本来想滑到左边,结果给直接Pop到上一个页面了,本篇就是寻找解决这个问题的方法。

实验一

首先想到了就是采用手势冲突的常规方法,代码如下:

if let gesture = self.navigationController?.interactivePopGestureRecognizer {
    gesture.require(toFail: self.collectionView.panGestureRecognizer)
}
复制代码

实验结果表明,这个方法的效果等于是禁用了侧滑返回的手势,因为水平方向UIScrollView的panGestureRecognizer不会失败,可能有人立马想到bounces属性,添加上self.collectionView.bounces = false这一句试试效果,结果证明,此方法还是行不通。

实验二

上面简单的方法不行,那就要从拦截手势事件着手了,可以从UIScollView的panGesture或者侧滑手势两个方向尝试,我个人比较倾向于从侧滑手势方向,所以接下来就记录下这个方向的实验过程。

在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的代理,然后在适当的地方再还原,在上面的代码中添加如下代码:

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

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
 self.navigationController?.interactivePopGestureRecognizer?.delegate = popGestrueDelegate
}
复制代码

实验结果表明,上面的方法解决了侧滑和水平方向滑动的手势冲突问题,但是看看代码感觉还不是特别完美,如果其他页面也需要解决这个手势冲突呢,那就需要把这些代码copy过去了,于是继续试验。

试验三

既然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)
}
复制代码

添加之后看看效果,嗯,效果还不错。

后记

开始时说过还可以从UIScrollView的手势着手,有兴趣的同学可以去尝试一下,如果有其他的更好方法,也欢迎留言讨论。

如需转载,请告知作者并注明作者和来源,谢谢!

注:

1、gif制作工具,PicGIF

你可能感兴趣的:(iOS侧滑返回和UIScrollView手势冲突如何解决)