iOS7 之前,滑动返回这个事儿是不被官方支持的,开发者需要自己实现,进入 iOS7 时代,苹果为了提升用户体验,增加了边缘侧滑的手势。注意这里的边缘,如果需要实现全屏滑动返回 还是需要开发者自己来实现的。
注意侧滑返回在以下几种情况下是会失效的:
- 隐藏导航栏:
isNavigationBarHidden = true
- 隐藏返回按钮:
navigationItem.hidesBackButton = true
- 自定义了:
leftBarButtonItem 或者 leftBarButtonItems
基于自定义导航栏下解决边缘侧滑失效的问题
我们知道,侧滑返回 的实现基于 UINavigationController
的手势 interactivePopGestureRecognizer
,我们可以通过代理这个手势达到我们的目的。
边缘侧滑返回
首先自定义一个继承字 UINavigationController
的子类:
class SMNavigationViewController: UINavigationController { |
然后实现手势的代理协议:
extension SMNavigationViewController: UIGestureRecognizerDelegate { |
到这里,我们已经实现了边缘侧滑返回的功能了。
当然,上面这种基本适用于因为导航栏导致的侧滑失效的问题了,但并不排除还有其他的解决办法。
例如返回按钮,可以自定义返回按钮等。
例如 TZScrollViewPopGesture
这个库,他不仅支持了边缘侧滑返回,更主要的是他解决了因为 scrollView 导致边缘侧滑失效的问题。
还有 FDFullscreenPopGesture
这个库,主要是实现了全屏侧滑的功能。
灵活控制侧滑功能
如果我们还想灵活控制某些页面不支持侧滑返回的话,可以自定义一个协议:
protocol SMPopGestureDelegate: NSObjectProtocol { |
在不需要侧滑返回的控制器:
extension XxController: SMPopGestureDelegate{ |
然后在 interactivePopGestureRecognizer 的代理方法:
extension SMNavigationViewController: UIGestureRecognizerDelegate { |
当手势冲突时边缘侧滑返回失效的问题
在解决因手势冲突造成的侧滑返回失效的问题之前,我们先来了解下手势的优先级以及 UIGestureRecognizerDelegate 的几个方法。
手势优先级
如果有两个手势,我们需要设置优先级的时候,就会调用方法:
func require(toFail otherGestureRecognizer: UIGestureRecognizer) |
这个方法的作用就是在两个有可能冲突的手势之间创建一个关系,只有当 otherGestureRecognizer 手势失败的时候,才响应手势,例如同时添加的单击手势和双击手势的时候,当双击手势响应失败的时候才响应单击手势:
let singalTap = UITapGestureRecognizer(target: self, action: #selector(onSingalTap)) |
一些手势代理方法
一般手势识别:
// 开始进行手势识别时调用的方法,返回 false 则结束,不再触发手势 |
手势同时识别:
// 是否允许同时识别两个手势,默认返回 false, 返回 true 表示这两个手势同时识别 |
失败控制:
// 当同时识别到另一个手势时,是否要求该手势失败 |
侧滑冲突的解决办法
什么情况下边缘侧滑会因为手势冲突时失效呢?这很好回答:
Web Controller· 失效,webView 页面比较特殊,不仅右滑返回会失效,自定义的手势也会全部失效,因为
WebView` 中已经内部集成了点击、滑动等多个手势,上面的方法在 ·UIWebView· 中并没有作用,解决办法是允许多个手势并发的代理方法,即extension WebViewController {
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}当使用
UIScrollView
、UITableView
、UICollectionView
等滑动视图的左右滑动时,滑动手势就会和右滑返回的手势冲突,右滑返回就会失效。
针对上述第二种的解决办法:
因为侧滑手势的优先级最大,所以可以在 NavigationController 中:
extension SMNavigationViewController: UIGestureRecognizerDelegate {
// 侧滑优先级最大,当手势冲突时,直接让另一个手势失败,优先响应侧滑返回
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
}
当然,具体的情况可能还需要结合自己的业务来决定。
2. 针对这种情况,有一种是直接在 controller 设置手势的优先级:
if let gesture = self.navigationController?.interactivePopGestureRecognizer {
scrollView.panGestureRecognizer.require(toFail: gesture)
}
但是我并没有获得成功,原因暂未可知 o(╥﹏╥)o
- 使用
TZScrollViewPopGesture
库,主要思路是实现一个自己的侧滑手势 - 还有另外一种奇淫技巧:
在 vc 的 view 的左侧贴一层宽 10 长 view 长度的透明 view
但是这种我并没有实验,有兴趣的可以试试 (* ̄︶ ̄)