前言
前一段时间换了工作,公司项目赶得比较紧,没有时间更新文章,现在闲下来了,赶紧写一篇来弥补自己的羞愧。
今天我们来重点讨论导航栏返回的问题,包括各种问题的解决方案。
系统默认导航栏的返回按钮和返回方式
在默认情况下,导航栏返回按钮长这个样子

导航栏左上角的返回按钮,其文本默认为上一个ViewController的标题,如果上一个ViewController没有标题,则为Back(中文环境下为“返回”)。
在默认情况下,导航栏返回的点击交互和滑动交互如下

这些东西不需要任何设置和操作,因此也没有其他需要说明的地方。
自定义左上角的返回按钮
绝大多数情况下,我们都需要根据产品需求自定义左上角的返回按钮,虽然这对大多数开发者来说不是什么难事,但依然有几个问题值得注意。
替换左上角返回按钮
替换返回按钮非常简单,只需要在ViewController中创建一个UIBarButtonItem和一张图片,并为按钮添加相应的点击事件即可,代码如下
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
UIButton * leftBtn = [UIButton buttonWithType:UIButtonTypeSystem];
leftBtn.frame = CGRectMake(0, 0, 25,25);
[leftBtn setBackgroundImage:[UIImage imageNamed:@"nav_back"] forState:UIControlStateNormal];
[leftBtn addTarget:self action:@selector(leftBarBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc]initWithCustomView:leftBtn];
}
- (void)leftBarBtnClicked:(UIButton *)btn
{
[self.navigationController popViewControllerAnimated:YES];
}
我们来看一眼效果

调整按钮位置
我们可以看到,上面的按钮是有点偏右的,那如果我们想调整按钮的位置该怎么做呢?设置Frame显然是行不通的,因为导航栏的NavigationItem是个比较特殊的View,我们无法通过简单的调整Frame来的调整左右按钮的位置。但是在苹果提供的UIButtonBarItem
中有个叫做UIBarButtonSystemItemFixedSpace
的控件,利用它,我们就可以轻松调整返回按钮的位置。具体使用方法如下
//创建返回按钮
UIButton * leftBtn = [UIButton buttonWithType:UIButtonTypeSystem];
leftBtn.frame = CGRectMake(0, 0, 25,25);
[leftBtn setBackgroundImage:[UIImage imageNamed:@"icon_back"] forState:UIControlStateNormal];
[leftBtn addTarget:self action:@selector(leftBarBtnClicked:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem * leftBarBtn = [[UIBarButtonItem alloc]initWithCustomView:leftBtn];;
//创建UIBarButtonSystemItemFixedSpace
UIBarButtonItem * spaceItem = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
//将宽度设为负值
spaceItem.width = -15;
//将两个BarButtonItem都返回给NavigationItem
self.navigationItem.leftBarButtonItems = @[spaceItem,leftBarBtn];
我们来看一眼效果

可以看到,我们的返回按钮已经紧靠着屏幕边缘。
这个方法同样适用于调整导航栏右侧的按钮
让滑动返回手势生效
如果使用自定义的按钮去替换系统默认返回按钮,会出现滑动返回手势失效的情况。解决方法也很简单,只需要重新添加导航栏的interactivePopGestureRecognizer
的delegate
即可。
首先为ViewContoller添加UIGestureRecognizerDelegate
协议
然后设置代理
self.navigationController.interactivePopGestureRecognizer.delegate = self;
至此,我们已经将返回按钮替换为我们的自定义按钮,并使滑动返回重新生效。接下来,我们继续来解决交互上的问题。
全屏滑动返回
这个一个很常见的需求,网上解决方案也很多,这里将本人常用的方法贴到这里。仅供参考
实现全屏滑动返回仅需在导航栏给导航栏添加UIGestureRecognizerDelegate
协议,并在ViewDidLoad中写入如下代码
// 获取系统自带滑动手势的target对象
id target = self.interactivePopGestureRecognizer.delegate;
// 创建全屏滑动手势,调用系统自带滑动手势的target的action方法
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:target action:@selector(handleNavigationTransition:)];
// 设置手势代理,拦截手势触发
pan.delegate = self;
// 给导航控制器的view添加全屏滑动手势
[self.view addGestureRecognizer:pan];
// 禁止使用系统自带的滑动手势
self.interactivePopGestureRecognizer.enabled = NO;
我们来看一眼效果(注意鼠标位置)

成功
这种方法的原理其实很简单,其实就是自定义一个全屏滑动手势,并将滑动事件设置为系统滑动事件,然后禁用系统滑动手势即可。
handleNavigationTransition
就是系统滑动的方法,虽然系统并未提供接口,但是我们我们可以通过runtime找到这个方法,因此直接调用即可。两位,不必担心什么私有API之类的问题,苹果如果按照方法名去判断是否使用私有API,那得误伤多少App。
NavigationBar切换动画的“终极解决方案”
本部分文字代码都较多,不想看这么多废话的同学请直接翻到末尾,文末附有下载地址,导入项目后,继承即可生效。
在改变了导航栏样式,实现了全屏滑动返回之后,我们有了一个看起来还不错的导航栏。但是我们滑动时的切换依然是系统自带的动画,如果遇到前一个界面的NavigationBar为透明或前后两个Bar颜色不一样,这种渐变式的动画看起来就会不太友好,尤其当前后两个界面其中一个界面的NavigationBar
为透明或隐藏时,