再看返回按钮(适配IOS11)

文章结构


1.现有的几种方式
2.最终方式

文章更新记录


2017-01-06日

1. 添加注意事项

一、现有的统一定制方式


从实现原理上来讲只有三种:

第一种:

使用UIBarButtonItem的两个函数setBackButtonBackgroundImagesetBackButtonTitlePositionAdjustment

setBackButtonBackgroundImage 用来设置返回按钮的背景图片
setBackButtonTitlePositionAdjustment 用来设置返回标题的位置,使其偏移出屏幕。

    UIImage *backButtonImage = [[UIImage imageNamed:@"nav_icon_back"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 25, 0, 0)];
    [[UIBarButtonItem appearance] setBackButtonBackgroundImage:backButtonImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
    [[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake(NSIntegerMin, NSIntegerMin) forBarMetrics:UIBarMetricsDefault];

优点:
1. 可以写在appdelegate里面,app启动的时候统一设置。

缺点:
1.IOS11下自定义的图片会向下偏移。
2.这里只是设置了title的偏移使其偏移出屏幕,title所占的空间依然存在。

WX20171216-142041@2x.png

不考虑,建议用下面两种方式。

第二种

使用IOS7系统后提供的backIndicatorImagebackIndicatorTransitionMaskImage属性。

image.png

基类UINavgationController里调用,或者基类控制器里面调用都行。
基于程序的运行效率建议放到基类UINavgationControllerviewDidload里调用,毕竟只要设置一次。

BSENavigationController.m
self.navigationController.navigationBar.backIndicatorImage = [UIImage imageNamed:@"backPic"];
self.navigationController.navigationBar.backIndicatorTransitionMaskImage = [UIImage imageNamed:@"backPic"];

基类控制器里调用

BSEViewController.m
UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
    [self.navigationItem setBackBarButtonItem:backButtonItem];

UIBarButtonItem属性说明:

ios自定义图片替换系统导航栏返回按钮样式这篇文章种提到一种方式:

在基类UINavgationController里实现UINavigationBarDelegate 方法如下:

- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPushItem:(UINavigationItem *)item{

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

item.backBarButtonItem = back;

return YES;
}

能够实现,但是有两个潜在的问题:

1.部分二级以后的界面标题重新出现
2.部分界面有一定的概率会出现返回键消失的情况

这里考虑到不是所有控制器都会继承基类,特别是引入的第三方库。所以不采用基类控制器的写法:
直接上代码:

BSENavigationController.h

#import <UIKit/UIKit.h>
#import "UIViewController+WLBackButton.h"
@interface BSENavigationController : UINavigationController

@end

BSENavigationController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setUpNavitems];
    [self setupNavigationBarTheme];
    // Do any additional setup after loading the view.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

-(void)setUpNavitems{
    UIImage *backButtonImage = [[UIImage imageNamed:@"nav_icon_back"] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
    self.navigationBar.backIndicatorImage = backButtonImage;
    self.navigationBar.backIndicatorTransitionMaskImage = backButtonImage;
}

#pragma mark - 设置navbar的字体和背景
- (void)setupNavigationBarTheme {
    //设置全局navBar字体颜色与背景颜色
    UINavigationBar *navBar = [UINavigationBar appearance];
    //设置字体颜色
    NSDictionary *attrNavBar = @{NSFontAttributeName : [UIFont systemFontOfSize:16.0f], NSForegroundColorAttributeName : HEXCOLOR(0x111111)};
    [navBar setTitleTextAttributes:attrNavBar];
    //设置背景颜色 首页导航透明
//    [navBar lt_setBackgroundColor:[UIColor whiteColor]];
    //设置背景颜色一般的方法
    [navBar setBarTintColor:[UIColor whiteColor]];
    if ([UIDevice currentDevice].systemVersion.integerValue >= 8.0) {
        [navBar setTranslucent:NO];
    }
}

UIViewController+WLBackButton.h

#import <UIKit/UIKit.h>
@interface UIViewController (WLBackButton)
@end

UIViewController+WLBackButton.m

#import "UIViewController+WLBackButton.h"
#import <objc/runtime.h>

@implementation UIViewController (WLBackButton)


+ (void)load{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        SEL originalSelector = @selector(viewDidLoad);
        SEL swizzledSelector = @selector(wl_viewDidLoad);
        
        Method originalMethod = class_getInstanceMethod(class, originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
        
        BOOL success = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (success) {
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        } else {
            method_exchangeImplementations(originalMethod, swizzledMethod);
        }
    });
}

-(void)wl_viewDidLoad{
    //执行原有方法
    [self wl_viewDidLoad];
    [self wl_setUpNavItems];
}

-(void)wl_setUpNavItems{
    UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil];
    [self.navigationItem setBackBarButtonItem:backButtonItem];
}

ok,完美解决问题。
该方法从原理上来讲只是替换系统原有的返回指示图片,然后在控制器里定义了标题大小为空的backbutton。不覆盖系统原有的返回事件,也不会导致系统的滑动返回失效。再则部分界面需要定制退出拦截提示时也比较好处理。推荐!!!!

第三种

设置控制器的leftBarButtonItem

//创建一个UIButton
UIButton *backButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 0, 40, 40)];
//设置UIButton的图像
[backButton setImage:[UIImage imageNamed:@"left_select_img.png"] forState:UIControlStateNormal];
//给UIButton绑定一个方法,在这个方法中进行popViewControllerAnimated
[backButton addTarget:self action:@selector(backItemClick) forControlEvents:UIControlEventTouchUpInside];
//然后通过系统给的自定义BarButtonItem的方法创建BarButtonItem
UIBarButtonItem *backItem = [[UIBarButtonItem alloc]initWithCustomView:backButton];
//覆盖返回按键
self.navigationItem.leftBarButtonItem = backItem;

典型的使用方法:

BSENavigationController.m

#pragma mark - 全局返回键以及抽屉事件设置
-(void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated{
    if(self.childViewControllers.count > 0){
        //设置返回键
        [self setUpBackBtn:viewController];
        //隐藏tabbar
        viewController.hidesBottomBarWhenPushed = YES;
    }
    [super pushViewController:viewController animated:animated];
}

#pragma mark - 返回键样式设置
-(void)setUpBackBtn:(UIViewController *)Controller
{
    UIButton *LeftItem = [UIButton new];
    LeftItem.frame = CGRectMake(0, 0, 25, 35);
    [LeftItem.imageView setContentMode:UIViewContentModeLeft];
    LeftItem.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
    [LeftItem setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];
    [LeftItem addTarget:self action:@selector(BackBtnClick) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *customLeftItem = [[UIBarButtonItem alloc] initWithCustomView:LeftItem];
    Controller.navigationItem.leftBarButtonItem = customLeftItem;
}

-(void)BackBtnClick{
    [self popViewControllerAnimated:YES];
}

优点:1.定制比较方便
缺点:1.内容会相对偏右,需要代码调整。
2.如果写在基类导航控制器里面,部分控制器要拦截返回事件就会比较麻烦,写在基类控制器内又会比较局限。
3.这种方法会失去手势滑动返回的功能 要能使用需要特殊的处理才行

既然是再看返回按钮,这种方式采用的比较多。这里下文也会贴出相应的解决办法。

问题一:内容会相对偏右,需要代码调整
 UIButton *btnBack = [UIButton new];
    [btnBack setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
    [btnBack setContentEdgeInsets:UIEdgeInsetsMake(0, -10, 0, 0)];
问题二:部分界面返回拦截问题
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self LVSetUpNavItem];
}

-(void)LVSetUpNavItem{
    self.navigationItem.leftBarButtonItem = nil;//移除原有的统一定制的返回键
    UIButton *LeftItem = [UIButton new];
    LeftItem.frame = CGRectMake(0, 0, 24, 24);
    [LeftItem.imageView setContentMode:UIViewContentModeCenter];
    [LeftItem setImage:[UIImage imageNamed:@"back"] forState:UIControlStateNormal];
    [LeftItem setBackgroundImage:[UIImage imageNamed:@"nav_fanhui"] forState:UIControlStateNormal];
    [LeftItem addTarget:self action:@selector(LVbackBtnClick) forControlEvents:UIControlEventTouchUpInside];
    UIBarButtonItem *customLeftItem = [[UIBarButtonItem alloc] initWithCustomView:LeftItem];
    self.navigationItem.leftBarButtonItem = customLeftItem;
}

-(void)LVbackBtnClick{
    NSNumber *value = [NSNumber numberWithInt:UIInterfaceOrientationPortrait];
    [[UIDevice currentDevice] setValue:value forKey:@"orientation"];
    [self.navigationController popViewControllerAnimated:YES];
}

问题三:系统滑动返回事件失效问题

这里工程里面用的是UINavigationController+FDFullscreenPopGesture这个工具类所以不存在这个问题,这里还是贴出处理代码。感兴趣的朋友们可以自己看下这边文章ios全局返回按钮和全屏侧滑功能

  1. 当重写了系统的push方法时,系统的侧滑功能就会失效.
  2. 侧滑功能实现原理:当用户使用侧滑功能的时候,系统是通过代理去通知实现侧滑功能,但是如果重写了push,那么代理知道,既然重写了push方法,那么代理就会通知系统不要去实现侧滑功能,这样就使得侧滑功能失效,或者产生bug.
  3. 思路:我们就通过对代理的控制,来实现对侧滑功能的控制
    3.1 设置一个属性,用来保存在push方法重写之前的代理
//设置一个属性保存系统的代理
@property (nonatomic, strong) id popDelegate;
//设置一个属性保存系统的代理
@property (nonatomic, strong) id popDelegate;

3.2 在重写的push方法中,让代理等于空—-意思是不让代理去通知系统侧滑功能失效

self.interactivePopGestureRecognizer.delegate = nil;

3.3 在代理方法中,将原来保存的代理属性,赋值给系统,让系统代理保持原来的工作

#pragma mark - 实现代理方法

- (void)navigationController:(UINavigationController *)navigationController didShowViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    //判断控制器是否为根控制器
    if (self.childViewControllers.count == 1) {
        //将保存的代理赋值回去,让系统保持原来的侧滑功能
        self.interactivePopGestureRecognizer.delegate = self.popDelegate;
    }
}

三、注意事项


问题1:UIViewController+BackButtonItemTitle导致标题文字出现

问题:发现部分界面返回标题又出现了,检查所有UIViewController相关分类发现UIViewController+BackButtonItemTitle.m具体内部实现就不贴出来了,删除即可。

参考文献:
ios全局返回按钮和全屏侧滑功能
改iOS返回按钮的几种方式
iOS拦截导航栏返回按钮事件的正确方式
简书App适配iOS 11
iOS关于设置一个自定义的导航控制器返回按钮
ios自定义图片替换系统导航栏返回按钮样式
Custom back indicator image and iOS 11
SFEmptyBackButton
自定义iOS的Back按钮(backBarButtonItem)和pop交互手势(interactivepopgesturerecognizer)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,900评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,068评论 4 62
  • Swift版本点击这里欢迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh阅读 25,342评论 7 249
  • 满篇的三生三世,我只记得夜华的那句:浅浅,过来。 这世间再没俊疾山上的素素了,那不过是青丘之国的白浅上神做的一场梦...
    月月were阅读 513评论 0 0
  • 2017年7月6日星期四,天气:晴 今天下午学完看图写话回到家,爷爷神神秘秘地对我说:“今天晚上你们要到外面去吃饭...
    Molly_c1e1阅读 697评论 0 0