最近在开发中遇到这样一个问题,有一个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