2.UINavigationController的backBarButtonItem、leftBarButtonItem

现象

在使用iOS导航栏时踩上的一些坑,假设两个UIViewController:A、B,B是A的子级视图控制器

  1. 在B上自定义返回键backBarButtonItem无效及界面异常
  2. 修改所有UIViewController导航栏的按钮

原因及解决方法

  1. 问题一:在B上自定义返回键backBarButtonItem无效及界面异常,可能原因解决方法如下:
  • 修改返回键标题无效,在A中设置了UIViewController的navigationItem.title,如下图,不设置时,该值默认是nil;如果该A先入栈,而后B入栈,此时栈顶的B的返回键自动被设置为A的navigationItem.title的值,如果A的title为nil,则B的返回键会使用“Back”;
    navigationItem.title的描述

    backBarButtonItem设置的是下一级navigation的返回键
  • 重新定义返回键无效,如果要修改B中的backBarButtonItem,可以创建一个新的UIBarButtonItem来替换,直接修改backBarButtonItem的title是无效的
    backBarButtonItem的描述

    推荐使用- (instancetype)initWithCustomView:(UIView *)customView;来自定义不同的样子;但是如果只是想去掉backBarButtonItem,则可以使用一个空的UIBarButtonItem来替换
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleDone target:nil action:nil];

补充:如果不修改内容,就是装饰一下文本,可以使用setTitleTextAttributes:forState:

  • 重定义的返回键标题重叠,涉及到leftBarButtonItem,这个的使用场景有点像淘宝和微信的文章,使用在导航栏左侧需要多个按钮时,比如‘Back’、‘Close’多个并列存在的情况,也可以用leftBarButtonItem来设置为一个返回键,但是需要设置backBarButtonItem为空或使用hidesBackButton隐藏 (2017.09.04修改),不然会出现两个标题重叠的情况,leftBarButtonItem的设置方法参考backBarButtonItem的设置方法,还可以批量设置多个,使用leftBarButtonItems。
    leftBarButtonItem和leftBarButtonItems
  1. 问题二:修改所有UIViewController导航栏的按钮
    实际运用中,像导航栏这样几乎所有UIViewController都会用到的东西基本应该在项目开始就准备后,如果要修改属性,秉持优化代码的精神,也应该在一处修改,然后在所有其他地方都生效,因此,这里有两种方法:
  • 继承UIViewController重写导航栏,继承UIViewController,自定义一个导航栏,隐藏系统的导航栏,然后在需要导航栏的地方用一句代码实现创建导航栏。
    如我使用过的一个:QHBasicViewController(来源已不可考)
    QHBasicViewController.h
#import <UIKit/UIKit.h>

@interface QHBasicViewController : UIViewController

@property (nonatomic, strong) UILabel* navTitleLabel;///导航栏标题
@property (nonatomic, strong, readonly) UIView *navView;///导航栏,只读
@property (nonatomic, strong) UIImageView* navBackgroundView;///导航栏背景
@property (nonatomic, strong, readonly) UIView* leftV;///导航栏左侧View,只读
@property (nonatomic, strong, readonly) UIView *rightV;///导航栏右侧View,只读

/** 创建导航栏
 * @param szTitle 导航栏标题
 * @param menuItem block回调,需要在这里定义导航栏上的各个按钮
 */
- (void)createNavWithTitle:(NSString *)szTitle createMenuItem:(UIView *(^)(int nIndex))menuItem;

@end

QHBasicViewController.m

#import "QHBasicViewController.h"
#import "HHUtil.h"

#define StatusbarHeight ((isIos7 >= 7 && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1)?20.f:0.f)

#pragma mark - QHBasicViewController
@interface QHBasicViewController ()
{
}

@end

@implementation QHBasicViewController


- (void)viewWillAppear:(BOOL)animated
{
    //隐藏UIViewController的导航栏
    [[self navigationController] setNavigationBarHidden:YES];
    [super viewWillAppear:TRUE];
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
}

- (void)createNavWithTitle:(NSString *)szTitle createMenuItem:(UIView *(^)(int nIndex))menuItem
{
    const CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width;
    const CGFloat navigationViewHeigth = 44.f;
    const CGFloat _nSpaceNavY = (isIos7 >= 7 && __IPHONE_OS_VERSION_MAX_ALLOWED > __IPHONE_6_1) ? 0 : 20;
    UIImageView *navIV = [[UIImageView alloc] initWithFrame:CGRectMake(0, _nSpaceNavY, screenWidth, 64 - _nSpaceNavY)];
    [navIV setImage:[UIImage imageNamed:@"bg_nav.png"]];
    _navBackgroundView = navIV;
    [self.view addSubview:navIV];
    
    /* { 导航条 } */
    _navView = [[UIImageView alloc] initWithFrame:CGRectMake(0.f, StatusbarHeight, screenWidth, navigationViewHeigth)];
    ((UIImageView *)_navView).backgroundColor = [UIColor clearColor];
    [self.view addSubview:_navView];
    _navView.userInteractionEnabled = YES;
    //
    if (szTitle != nil)
    {
        UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake((_navView.bounds.size.width - 240)/2, (_navView.bounds.size.height - 40)/2, 240, 40)];
        [titleLabel setText:szTitle];
        [titleLabel setTextAlignment:NSTextAlignmentCenter];
        [titleLabel setTextColor:[UIColor whiteColor]];
        [titleLabel setFont:[UIFont boldSystemFontOfSize:18]];
        [titleLabel setBackgroundColor:[UIColor clearColor]];
        titleLabel.numberOfLines = 2;
        _navTitleLabel = titleLabel;
        [_navView addSubview:_navTitleLabel];
    }
    //
    UIView *item1 = menuItem(0);
    if (item1 != nil)
    {
        _leftV = item1;
        [_navView addSubview:item1];
    }
    UIView *item2 = menuItem(1);
    if (item2 != nil)
    {
        _rightV = item2;
        [_navView addSubview:item2];
    }
}
@end

在创建你自己的UIViewController的时候,需要做的就是继承QHBasicViewController而不是UIViewController,然后在viewDidLoad的时候createNavWithTitle: createMenuItem:创建导航栏,然后在块回调中定义你自己的按钮。

  • 继承UINavigationController重写push方法,继承UINavigationController方法,重写push方法,在push前后统一修改backBarButtonItem、leftBarButtonItem和rightBarButtonItem,甚至其它的导航栏设置,比如自定义titleView,导航栏背景之类的。
    奉上个人做的一个例子:
    HHNavigationController.h
#import <UIKit/UIKit.h>

@interface HHNavigationController : UINavigationController

/** 重载的initWithRootViewController:方法,附带初始化导航栏
 * @param rootViewController 视图控制器
 */
- (instancetype)initWithRootViewController:(UIViewController *)rootViewController;

@end

HHNavigationController.m

#import "HHNavigationController.h"
#import <MediaPlayer/MediaPlayer.h>

@interface HHNavigationController ()<UIGestureRecognizerDelegate>

@property (nonatomic, strong) UIBarButtonItem *backBarButtonItm;

@end

@implementation HHNavigationController

- (instancetype)initWithRootViewController:(UIViewController *)rootViewController
{
    self = [super initWithRootViewController:rootViewController];
    if (self) {
        //设置导航栏和状态栏字体颜色为白色,导航栏背景设置为绿色
        rootViewController.navigationController.navigationBar.barTintColor = RGBA(129, 164, 128, 1);//导航栏为绿色
        rootViewController.navigationController.navigationBar.barStyle = UIBarStyleBlack;//状态栏字体为白色
        rootViewController.navigationController.navigationBar.translucent = NO;//导航栏不透明
        //设置侧滑滑动返回
        self.interactivePopGestureRecognizer.delegate = self;
    }
    return self;
}

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

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

#pragma mark - action
-(void)onBack
{
    [self popViewControllerAnimated:YES];
}

#pragma mark - back button
-(UIBarButtonItem *)backBarButtonItm
{
    if (!_backBarButtonItm) {
        UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        backBtn.frame = CGRectMake(0, 0, 30, 30);
        [backBtn setImage:[UIImage imageNamed:@"btn_back"] forState:UIControlStateNormal];
        [backBtn addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside];
        UIBarButtonItem *backBarItm = [[UIBarButtonItem alloc] initWithCustomView:backBtn];
        _backBarButtonItm = backBarItm;
    }
    return _backBarButtonItm;
}

#pragma mark - Override
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
    viewController.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:nil style:UIBarButtonItemStyleDone target:nil action:nil];//返回键置空
    [super pushViewController:viewController animated:animated];
    if (viewController.navigationItem.leftBarButtonItem == nil && [self.viewControllers count] > 1) {
        //设置返回键
        viewController.navigationItem.leftBarButtonItem = self.backBarButtonItm;
        //设置导航栏和状态栏字体颜色为白色,导航栏背景设置为绿色
        viewController.navigationController.navigationBar.barTintColor = RGBA(129, 164, 128, 1);//导航栏为绿色
        viewController.navigationController.navigationBar.barStyle = UIBarStyleBlack;//状态栏字体为白色
        viewController.navigationController.navigationBar.translucent = NO;//导航栏不透明
    }
}
@end

缺点

  • 原生的导航栏虽然方便,但是牺牲了很多便利,自定义起来比较麻烦,特别是如果要求动画的话(这个需求很正常),基本就用不了只能自己写一个伪导航栏来替代
  • 不容易统一,如果使用present方法,呈现的方式都不同,也不会有导航栏,虽然有方法加导航栏,但是还是有差异,也容易给代码的编写造成困扰
  • 难定制,遇到定制需求,由于backBarButtonItem本身要求的在加载到bar之前设置好的限制,很难动态的变化

修改

2017.09.02 16:13 V1.0 Create
2017.09.04 09:36 V1.1 添加backBarButtonItem的隐藏方法介绍

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,110评论 4 62
  • 1,Search Bar 怎样去掉背景的颜色(storyboard里只能设置background颜色,可是发现cl...
    以德扶人阅读 2,359评论 2 50
  • #iOS开发之UI篇#iOS开发之UI篇 #常用控件介绍1## #UI第09天:滚动视图# ##UIScrollV...
    LennonLin阅读 1,769评论 0 0
  • MVC 我们学习了koa处理url,Nunjucks渲染模块,但我们去结合着两个的时候。当一个用户请求URL,ko...
    _panda阅读 1,408评论 0 0
  • 像迷路的南迁鸟儿 一心飞往温暖的南方 没有方向的旅行 还忘却了路上的风景 像在旅途发呆的鸟儿 任秋风带走思绪 剩下...
    路过且错过阅读 346评论 0 3