ios开发返回按钮消失_关于iOS导航栏返回按钮问题的解决方法

news/2025/2/24 17:32:00

最近遇到一个关于导航栏返回按钮的问题,因为之前项目里面都是用的系统默认的返回按钮样式所以没有想过要去更改,后来有需要将返回按钮箭头旁边的文字去掉,同时将该返回按钮的点击事件重新定义。一开始尝试自定义按钮然后设置为leftBarButtonItem,但是这样图片可能跟系统自带的不一样,还有就是返回按钮的位置跟系统自带的不一样。后来找了一些资料,发现将文字去掉比较简单,一般做法是控制器中添加如下代码,然后他的下一级控制就有一个只有箭头没有文字返回按钮:

UIBarButtonItem *backBtn = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

self.navigationItem.backBarButtonItem = backBtn;

也可以创建一个根控制器在其中使用上述代码然后让其他控制器继承这个根控制器实现批量操作。但是如果遇到需要自定义该返回的点击事件的时候,在上面方法中添加target与action是不可行的,同时这种做法也会产生一个问题,就是实际的返回按钮点击区域总是比按钮看到的范围大,一般是距离箭头右边30距离都可点击。接下来我就带大家一起了解这些问题产生的原因以及如何更好的解决这些问题。

首先我们看一下按照上面代码去掉返回按钮文字之后的导航栏视图的结构层次,因为导航栏的视图加载以及初始化跟viewController的view不一样,不能再veiwDidLoad中去观察(viewWillAppear中也不行)要在viewDidLoad中才可以看到完整的导航栏视图结构层次。我们可以在一个有去掉文字的返回按钮控制器的viewDidLoad中打上断点然后在控制台执行:

po [[UIWindow keyWindow] recursiveDescription]

会得到如下输出:

; layer = >

| ; layer = >

| | >

| | | >

| | | | >

| | | | | <_uilayoutguide: frame="(0" hidden="YES;" layer="<CALayer:">>

| | | | | <_uilayoutguide: frame="(0" hidden="YES;" layer="<CALayer:">>

| | ; layer = >

| | | <_uinavigationbarbackground: frame="(0" opaque="NO;" autoresize="W;" userinteractionenabled="NO;" layer="<CALayer:">> - (null)

| | | | <_uibackdropview: frame="(0" opaque="NO;" autoresize="W+H;" userinteractionenabled="NO;" layer="<_UIBackdropViewLayer:">>

| | | | | <_uibackdropeffectview: frame="(0" clipstobounds="YES;" opaque="NO;" autoresize="W+H;" userinteractionenabled="NO;" animations="{" filters.colormatrix.inputcolormatrix="<CABasicAnimation:">; }; layer = >

| | | | | >

| | | | > - (null)

| | | >

| | | | >

| | | >

| | | | >

| | | <_uinavigationbarbackindicatorview: frame="(8" opaque="NO;" userinteractionenabled="NO;" layer="<CALayer:">> - Back

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

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

_UINavigationBarBackground:(UIImageView)导航栏背景图(不可以直接设置图片)

UINavigationItemView :(UIView)包含显示导航栏标题

UINavigationItemButtonView :(UIView)包含显示导航栏左视图(不可移除,更改大小,颜色,可以隐藏,决定了点击区域的大小)

_UINavigationBarBackIndicatorView :(UIImageView)导航栏返回按钮箭头图片(不可以修改图片)

_UINavigationBarBackIndicatorView就是返回按钮的箭头也就是我们需要保留的,UINavigationItemButtonView就是我们需要研究的也是我们前面提到问题的主要原因所在。再次看看这个对象在控制台的输出:

| | | >

| | | | >

这个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];

}

这样可以改变返回按钮的点击事件,此时想要解决点击范围只能在这个view上添加一个指定大小的按钮了。最后总结出来的解决办法就是创建一个viewController的分类:

UIViewController+CustomBackButton.h文件

#import

@interface UIViewController (CustomBackButton)

- (void)customNavBackButtonMethod;

@end

UIViewController+CustomBackButton.m文件

#import "UIViewController+CustomBackButton.h"

#import

@implementation UIViewController (CustomBackButton)

+ (void)load {

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

Class class = [self class];

SEL originalSelector = @selector(viewDidLoad);

SEL swizzledSelector = @selector(mm_viewDidLoad);

SEL originalSelector1 = @selector(viewDidAppear:);

SEL swizzledSelector1 = @selector(mm_viewDidAppear);

Method originalMethod = class_getInstanceMethod(class, originalSelector);

Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);

Method originalMethod1 = class_getInstanceMethod(class, originalSelector1);

Method swizzledMethod1 = class_getInstanceMethod(class, swizzledSelector1);

BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));

BOOL didAddMethod1 = class_addMethod(class, originalSelector1, method_getImplementation(swizzledMethod1), method_getTypeEncoding(swizzledMethod1));

if (didAddMethod) {

class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));

} else {

method_exchangeImplementations(originalMethod, swizzledMethod);

}

if (didAddMethod1) {

class_replaceMethod(class, swizzledSelector1, method_getImplementation(originalMethod1), method_getTypeEncoding(originalMethod1));

} else {

method_exchangeImplementations(originalMethod1, swizzledMethod1);

}

});

}

#pragma mark - Method Swizzling

- (void)mm_viewDidLoad {

[self mm_viewDidLoad];

UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];

[self.navigationItem setBackBarButtonItem:backButtonItem];

}

- (void)mm_viewDidAppear {

[self mm_viewDidAppear];

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(customNavBackButtonMethod) forControlEvents:UIControlEventTouchUpInside];

[nav_back addSubview:backButton];

}

}

- (void)customNavBackButtonMethod {

[self.navigationController popViewControllerAnimated:YES];

}

@end

在项目里面创建完以后就会生效。这里所做的就是在所有的viewController执行viewDidLoad的时候将返回按钮的文字置空,在执行viewDidAppear的时候添加一个自定义的按钮去响应pop事件。如果遇到需要自定义返回事件我在.h将返回事件开放出来,只要在对应的viewDidLoad中复写customNavBackButtonMethod方法就可以在点击返回按钮时执行到你复写的方法中了,好了,是不是感觉很简单呢。


http://www.niftyadmin.cn/n/712252.html

相关文章

java本地调用调试_远程调用cmd更新本地jar

/*** 重启完成更新*/publicboolean restart() {log.info("restart");String command "cmd /c start C:\\rct\\winsw\\rct\\update.bat";//测试String distName "dist";String jarName "rct-demo-1.0.jar";String bat;if (type.equals…

Toast实现源码解析

说明 本篇文章用于介绍Android中Toast的实现原理。和简单实现一个自定义的Toast. Toast实现 一般常用Toast格式为: Toast.makeText(context,"text.",Toast.LENGTH_LONG).show(); 就此&#xff0c;对Toast做一个了解.首先&#xff0c;Toast调用来了一个静态方法makeTe…

java fastjson解析json_java JSON解析库Alibaba Fastjson用法详解

本文实例讲述了java JSON解析库Alibaba Fastjson用法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;Json是一种轻量级的数据交换格式&#xff0c;应该在一个程序员的开发生涯中是常接触的。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写…

二元函数泰勒公式例题_高数031泰勒公式求极限 真香!

今天我们来看看泰勒公式的一些综合应用&#xff0c;先来如何利用泰勒公式求极限&#xff0c;有一些极限十分复杂难看&#xff0c;对函数密集恐惧症患者刺激巨大&#xff0c;这时候看似复杂的泰勒展开反而会将其巧妙化简&#xff0c;让问题迎刃而解。并且在原则上&#xff0c;任…

LVS-DR负载均衡-03

LB端脚本 # vim /usr/local/sbin/lvs-dr.sh #!/bin/bash#description : start LVS OF DIRECTORSERVER #GW192.168.28.253#WEBSITE DIRECTOR VIP WEB_VIP192.168.28.111 WEB_RIP1192.168.28.134 WEB_RIP2192.168.28.135 . /etc/rc.d/init.d/functions logger $0 called with $1 …

web前端学习(二十七)——CSS3导航栏的相关设置

1.CSS导航栏 垂直 水平 导航栏 熟练使用导航栏&#xff0c;对于任何网站都非常重要。 使用CSS你可以转换成好看的导航栏而不是枯燥的HTML菜单。 2.导航栏链接列表 作为标准的HTML基础一个导航栏是必须的&#xff0c;在我们的例子中我们将建立一个标准的HTML列表导航栏。 导航条…

ABP官方文档翻译 7.2 Hangfire集成

Hangfire集成 介绍ASP.NET Core集成ASP.NET MVC 5.x集成 面板授权介绍 Hangfire是一个综合的后台job管理器。你可以 把它集成到ABP&#xff0c;用来取代默认的后台job管理器。Hangfire可以使用相同的后台jobAPI。因此&#xff0c;你的代码与Hangfire是独立的。但是&#xff0c;…

vscode还用装git_VScode 搭配Git

VScode搭配Git一、Visual Studio Code安装Windows版下载地址2、安装一般默认下一步即可3、安装完&#xff0c;默认语言为英文&#xff0c;需要安装中文语言&#xff0c;步骤如下(1)点击左侧的小方块(拓展)(2)搜索框输入“Chinese”,点击“Install”(3)稍等一会&#xff0c;软件…