关于iOS导航栏返回按钮问题的解决方法

2020-01-15 14:53:47王振洲

直接看或许不容易懂,还需要结合Xcode的“Debug view Hierarchy”或者是Reveal工具看实际视图结构:

iOS,导航栏,返回按钮

我们可以看到在UINavigationBar中包含有4个类,它们大致的作用是:

_UINavigationBarBackground :(UIImageView)导航栏背景图(不可以直接设置图片)
UINavigationItemView :(UIView)包含显示导航栏标题
UINavigationItemButtonView :(UIView)包含显示导航栏左视图(不可移除,更改大小,颜色,可以隐藏,决定了点击区域的大小)
_UINavigationBarBackIndicatorView :(UIImageView)导航栏返回按钮箭头图片(不可以修改图片)
_UINavigationBarBackIndicatorView就是返回按钮的箭头也就是我们需要保留的,UINavigationItemButtonView就是我们需要研究的也是我们前面提到问题的主要原因所在。再次看看这个对象在控制台的输出:


 | | | <UINavigationItemButtonView: 0x8ab6c80; frame = (8 6; 41 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6d60>>
 | | | | <UILabel: 0x8ab6f10; frame = (-981 -995; 91 22); text = ''; clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x8ab6fb0>>

这个UINavigationItemButtonView应该是系统在这个view的draw方法里就决定frame,修改frame就触发needdisplay重新改变它的frame,因此这个view只会根据其上的label(也就是返回按钮显示的文字)的内容变化而改变宽度其余基本不可变,我们虽然将label的内容设置为空但是这个UINavigationItemButtonView的大小却并没有改变同时点击区域也没有改变。从控制台里的还可看到这个veiw的userInteractionEnabled属性为NO,如果说点击的是这个view,但现在这个view是不可交互的,只能说明是真正响应点击事件的是这个view背后的某个控件(视图结构和代码里都没有发现这个控件)。因此要想解决我之前提到的问题就得利用这个UINavigationItemButtonView,我们可以在viewDidAppear中取得到UINavigationItemButtonView(如果用遍历的方式获取会有一个延迟,因为这个view的位置固定为第三个所以我们就直接获取就可以了),将其userInteractionEnabled设置为yes并且在这个View上添加手势tap事件即可:


UIView *nav_back = [self.navigationController.navigationBar.subviews objectAtIndex:2];
if ([nav_back isKindOfClass:NSClassFromString(@"UINavigationItemButtonView")]) {
  nav_back.userInteractionEnabled = YES;
  // UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(backAction:)];
  // [nav_back addGestureRecognizer:tap];
  UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
  backButton.frame = CGRectMake(0, 0, 20, 21);
  [backButton addTarget:self action:@selector(customMethod) forControlEvents:UIControlEventTouchUpInside];
  [nav_back addSubview:backButton];
 }