iOS 一行代码自定义带导航栏的UIViewcontroller

系统的NavigationBar使用需要创建大量的代码,如果还要处理点击事件则代码量更大,并且一般情况下对于项目工期要求比较紧的情况下,就需要自定义NavigationBar下面是我自己经常用到的快速搭建自定义NavigationBar的demo,主要解决了以下问题


  • 1 一行代码创建带返回按钮+title的NavigationBar。
    • 2 两行代码创建带有leftBarButtonItems 、rightBarButtonItems 以及中间的title。
      • 3 快速配置处理左右item点击事件。
        • 4 空数据异常界面展示问题
          • 5 提供众多接口方便个性化NavigationBar定制

下面是我MABaseViewController的文件核心代码


MABaseViewController.h 文件
typedef void(^DEAL_EXP_ACTION)(void);

typedef NS_ENUM(int, NaviBarBtn) {
    NaviBarBtnLeft = 101,
    NaviBarBtnRight,
};
@interface MABaseViewController : UIViewController

- (void)setupStatusBarWithColor:(UIColor *)color;
- (void)setupStatusBarHidden:(BOOL)hidden;

- (void)setupNaviBarWithColor:(UIColor *)color;
- (void)setupNaviBarHidden:(BOOL)hidden;

- (void)setupNavLineBreakimgViewHidden:(BOOL)hidden;


- (void)setupNaviBarWithTitle:(NSString *)title;
- (void)setupNaviBarHiddenBtnWithLeft:(BOOL)left
                                right:(BOOL)right;
/**
 *  设置统一的默认的返回样式以及中间的title
 *
 *  @param title NavBar title 即 self.title 上面显示的内容
 */
- (void)setupNaviBarWithBackAndTitle:(NSString *)title;
/**
 *  设置导航栏的左右item
 *
 *  @param btnType 按钮类型
 *  @param text    按钮上显示的文字
 *  @param imgName   按钮上的小图标
 */
- (void)setupNaviBarWithBtn:(NaviBarBtn)btnType
                  titleText:(NSString *)text
                      image:(NSString *)imgName;
- (void)setupNaviBarWithCustomView:(UIView *)view;

- (CGFloat)getNaviBarHeight;
- (CGFloat)getContentHeight;

- (UIView *)getStatusBarView;
- (UIView *)getNaviBarView;
- (UILabel *)getTitleLabel;
- (UIButton *)getNavBarBtn:(NaviBarBtn)btnTag;



- (void)setUpShowExceptionViewWithImage:(UIImage *)tipImage
                                 topGap:(CGFloat )vGapDistance
                             dealAction:(DEAL_EXP_ACTION)action;

- (void)setUpHiddenExceptionView;

#pragma mark - son class should be  Overwrite method
/**
 *  provide customUI decorate
 */
- (void)customUI;

/**
 *  handle left/right custom navbar action
 *
 *  @param barbtn <#barbtn description#>
 */
- (void)handleBarBtnAction:(UIButton *)barbtn;


@end

MABaseViewController.m 文件
#import "MABaseViewController.h"
#import "MACommon.h"

#define kWIDTH   44.0

// CUSTOM NAV APPERANCE

#define kNAV_BAR_HEIGHT   40.0

#define STATUS_BAR_COLOR [UIColor redColor]

#define NAV_BAR_COLOR    [UIColor redColor]

#define NAV_TEXT_COLOR   [UIColor whiteColor]

#define NAV_TEXT_FONT    [UIFont systemFontOfSize:16.0]


@interface MABaseViewController ()
{
    CGFloat barSpacing;
}
@property (strong, nonatomic) UIView *statusBarView;
@property (strong, nonatomic) UIView *naviBarView;
@property (strong, nonatomic) UILabel *titleLabel;
@property (strong, nonatomic) UIButton *leftBtn;
@property (strong, nonatomic) UIButton *rightBtn;

@property (nonatomic, strong) UIImageView *bottomLineBreakImgView;

@property (nonatomic, strong) UIView *exceptionView;
@property (nonatomic, strong) UIImageView *expTipImgView;
@property (nonatomic, copy)   DEAL_EXP_ACTION exceptionAction;

@end

@implementation MABaseViewController

- (id)init {
    self = [super init];
    if (self) {
        //
        barSpacing = 0.0;
    }
    return self;
}

- (UIStatusBarStyle)preferredStatusBarStyle{
    return UIStatusBarStyleLightContent;
}

- (UIStatusBarAnimation)preferredStatusBarUpdateAnimation{
    return UIStatusBarAnimationFade;
}

- (void)initWithStatusBar {
    CGRect frame = CGRectZero;
    // The status bar default color by red color.
    frame = CGRectMake(0.0, 0.0, kScreenWidth, barSpacing);
    self.statusBarView = [[UIView alloc] initWithFrame:frame];
    [self.statusBarView setBackgroundColor:STATUS_BAR_COLOR];
    [self.view addSubview:_statusBarView];
}

- (void)initWithNaviBar {
    CGRect frame = CGRectZero;
    // The status bar default color by red color.
    frame = CGRectMake(0.0, barSpacing, kScreenWidth, kNAV_BAR_HEIGHT);
    self.naviBarView = [[UIView alloc] initWithFrame:frame];
    [self.naviBarView setBackgroundColor:NAV_BAR_COLOR];
    [self.view addSubview:_naviBarView];
    
    // Left button
    frame = CGRectMake(0.0, 0.0, kWIDTH, kNAV_BAR_HEIGHT);
    self.leftBtn = [[UIButton alloc] initWithFrame:frame];
    [self.leftBtn addTarget:self
                     action:@selector(handleBarBtnAction:)
           forControlEvents:UIControlEventTouchUpInside];
    [self.leftBtn setTitleColor:NAV_TEXT_COLOR forState:UIControlStateNormal];
    [self.leftBtn.titleLabel setFont:NAV_TEXT_FONT];
    [self.leftBtn setTag:NaviBarBtnLeft];
    [self.leftBtn setHidden:YES];
    [self.naviBarView addSubview:_leftBtn];
    
    // Right button
    frame = CGRectMake(CGRectGetWidth(_naviBarView.bounds) - kWIDTH, 0.0, kWIDTH, kNAV_BAR_HEIGHT);
    self.rightBtn = [[UIButton alloc] initWithFrame:frame];
    [self.rightBtn addTarget:self
                      action:@selector(handleBarBtnAction:)
            forControlEvents:UIControlEventTouchUpInside];
    [self.rightBtn.titleLabel setFont:NAV_TEXT_FONT];
    [self.rightBtn setTitleColor:NAV_TEXT_COLOR forState:UIControlStateNormal];
    [self.rightBtn setTag:NaviBarBtnRight];
    [self.rightBtn setHidden:YES];
    [self.naviBarView addSubview:_rightBtn];
    
    // Title label
    frame = CGRectMake(0.0, 0.0, 0.0, kNAV_BAR_HEIGHT);
    self.titleLabel = [[UILabel alloc] initWithFrame:frame];
    [self.titleLabel setBackgroundColor:[UIColor clearColor]];
    [self.titleLabel setLineBreakMode:NSLineBreakByWordWrapping];
    [self.titleLabel setFont:NAV_TEXT_FONT];
    [self.titleLabel setTextAlignment:NSTextAlignmentCenter];
    [self.titleLabel setTextColor:NAV_TEXT_COLOR];
    [self.naviBarView addSubview:_titleLabel];
    
    frame = CGRectMake(0, kNAV_BAR_HEIGHT - 1, kScreenWidth, 1);
    self.bottomLineBreakImgView = [[UIImageView alloc] initWithFrame:frame];
    UIImage *image = [[UIImage imageNamed:@"login_linebreak"] stretchableImageWithLeftCapWidth:2 topCapHeight:1];
    self.bottomLineBreakImgView.image = image;
    [self.naviBarView addSubview:_bottomLineBreakImgView];
    [self.bottomLineBreakImgView setHidden:YES];
    
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    self.edgesForExtendedLayout = UIRectEdgeNone;
    self.extendedLayoutIncludesOpaqueBars = NO;
    self.modalPresentationCapturesStatusBarAppearance = NO;
    
    barSpacing = 20.0;
    [self initWithStatusBar];
    
    // init navi bar
    [self initWithNaviBar];
    if ([self respondsToSelector:@selector(customUI)]) {
        [self customUI];
    }
    
}

#pragma mark - 判断是否有网络

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.view bringSubviewToFront:self.statusBarView];
    [self.view bringSubviewToFront:self.naviBarView];
    
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
}

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    
}

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

- (void)dealloc{//统一在此取消VC注册到通知中心的通知,防止出现崩溃问题。
    
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

#pragma mark - touchAction
#pragma mar
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
    [super touchesBegan:touches withEvent:event];
    [self.view endEditing:YES];
}

#pragma mark - Public method
- (void)setupStatusBarWithColor:(UIColor *)color {
    if (![color isEqual:_statusBarView.backgroundColor]) {
        [self.statusBarView setBackgroundColor:color];
    }
}

- (void)setupStatusBarHidden:(BOOL)hidden {
    [self.statusBarView setHidden:hidden];
}

- (void)setupNaviBarWithColor:(UIColor *)color {
    if (![color isEqual:_naviBarView.backgroundColor]) {
        [self.naviBarView setBackgroundColor:color];
    }
}

- (void)setupNaviBarHidden:(BOOL)hidden {
    [self.naviBarView setHidden:hidden];
}

- (void)setupNavLineBreakimgViewHidden:(BOOL)hidden{
    [self.bottomLineBreakImgView setHidden:hidden];
}

- (void)setupNaviBarWithTitle:(NSString *)title {
    if (_titleLabel && [title length] > 0) {
        [self.titleLabel setText:title];
        
        CGRect frame = _titleLabel.frame;
        //标题title 不能超过160 否则会影响界面展示效果
        frame.size.width = MIN([MACommon getLabelWidth:_titleLabel], 160);
        [self.titleLabel setFrame:frame];
        
        CGPoint center = CGPointMake(self.naviBarView.center.x, self.naviBarView.center.y - barSpacing);
        [self.titleLabel setCenter:center];
    }
}

- (void)setupNaviBarHiddenBtnWithLeft:(BOOL)left
                                right:(BOOL)right {
    if (left != _leftBtn.isHidden) {
        [self.leftBtn setHidden:left];
    }
    
    if (right != _rightBtn.isHidden) {
        [self.rightBtn setHidden:right];
    }
}

- (void)setupNaviBarWithBackAndTitle:(NSString *)title {
    [self setupNaviBarWithTitle:title];
    
    if ([[self.navigationController viewControllers] count] > 1) {
        [self setupNaviBarWithBtn:NaviBarBtnLeft
                        titleText:@""
                            image:@"navi_back_nor"];
    }
}

- (void)setupNaviBarWithBtn:(NaviBarBtn)btnType
                  titleText:(NSString *)text
                      image:(NSString *)imgName{
    UIButton *btn = nil;
    if (btnType == NaviBarBtnLeft) {
        btn = _leftBtn;
    } else if (btnType == NaviBarBtnRight) {
        btn = _rightBtn;
    }
    
    if (!btn) return;
    if ([btn isHidden]) [btn setHidden:NO];
    
    CGRect frame = btn.frame;
    UIImage *image = nil;
    if ([imgName length] > 0) {
        image = [UIImage imageNamed:imgName];
        [btn setImage:image forState:UIControlStateNormal];
        
        frame.size.width = MAX(image.size.width, 44.0);
    }
    
    if ([text length] > 0) {
        [btn setTitle:text forState:UIControlStateNormal];
        
        if (image) {
            frame.size.width = image.size.width + [MACommon getLabelWidth:btn.titleLabel] + 20.0;
        } else {
            frame.size.width = [MACommon getLabelWidth:btn.titleLabel] + 20.0;
        }
    }
    
    frame.size.width = MAX(CGRectGetWidth(frame), CGRectGetWidth(btn.frame));
    
    if (btn.tag == NaviBarBtnRight) {
        frame.origin.x = CGRectGetWidth(_naviBarView.bounds) - CGRectGetWidth(frame);
    }
    
    [btn setFrame:frame];
}

- (void)setupNaviBarWithCustomView:(UIView *)view {
    if (view) {
        //
        [self.titleLabel setHidden:YES];
        
        [self.naviBarView addSubview:view];
    }
}

- (CGFloat)getNaviBarHeight {
    if (_naviBarView && !_naviBarView.isHidden) {
        return barSpacing + CGRectGetHeight(_naviBarView.frame);
    }
    return barSpacing;
}

- (CGFloat)getContentHeight {
    return CGRectGetHeight(self.view.bounds) - [self getNaviBarHeight];
}

- (UIView *)getStatusBarView{
    return  self.statusBarView;
}

- (UIView *)getNaviBarView {
    return self.naviBarView;
}

- (UILabel *)getTitleLabel {
    return self.titleLabel;
}

- (UIButton *)getNavBarBtn:(NaviBarBtn)btnTag{
    if (btnTag == NaviBarBtnLeft) {
        return self.leftBtn;
    } else if(btnTag == NaviBarBtnRight){
        return self.rightBtn;
    }
    return nil;
}

- (void)setUpShowExceptionViewWithImage:(UIImage *)tipImage
                                 topGap:(CGFloat )vGapDistance
                             dealAction:(DEAL_EXP_ACTION)action{
    if (!tipImage) {
        NSLog(@"exception Tip image can not be nil !!!");
        return;
    }
    
    if (!self.exceptionView) {
        CGFloat v_distance =  CGRectGetMaxY(self.naviBarView.frame) + vGapDistance;
        CGRect frame = CGRectMake(0, v_distance, kScreenWidth, kScreenHeight - v_distance);
        self.exceptionView = [[UIView alloc] initWithFrame:frame];
        self.exceptionView.backgroundColor = [UIColor whiteColor];
        [self.view addSubview:_exceptionView];
    } else {
        self.exceptionView.hidden = NO;
    }
    [self.view bringSubviewToFront:_exceptionView];
    
    if (!self.expTipImgView) {
        self.expTipImgView = [[UIImageView alloc] init];
        [self.exceptionView addSubview:self.expTipImgView];
    }
    CGSize tipImgSize = tipImage.size;
    self.expTipImgView.image = tipImage;
    self.expTipImgView.bounds = CGRectMake(0, 0, tipImgSize.width, tipImgSize.height);
    CGPoint center = CGPointMake(kScreenWidth/2, CGRectGetHeight(self.exceptionView.frame)/2);
    center.y = center.y - 30 - vGapDistance/8;
    self.expTipImgView.center = center;
    
    if (action) {
        self.exceptionAction = action;
        [MACommon addTapGesturesWithView:self.expTipImgView target:self selector:@selector(exceptionImgTapAction:)];
    } else {
        self.exceptionAction = nil;
    }
    
}

- (void)setUpHiddenExceptionView{
    self.exceptionView.hidden = YES;
}

- (void)exceptionImgTapAction:(UITapGestureRecognizer *)tapGestureRecognizer{
    if (self.exceptionAction) {
        self.exceptionAction();
    }
}


#pragma mark - Public method
- (void)customUI{
    NSLog(@"son class should implement this Method!");
}

- (void)handleBarBtnAction:(UIButton *)btn {
    if (btn.tag == NaviBarBtnLeft) {
        if ([[self.navigationController viewControllers] count] != 1) {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }
}


@end

使用方法

#pragma mark - OverWirte
#pragma mark - 
//改方法的执行介于子类的viewDidLoad 之前,父类的viewDidLoad之后,用于界面子view的构建
- (void)customUI{
    //方式一
    [self setupNaviBarWithBackAndTitle:@"自定义navbar"];
    [self setupNaviBarWithBtn:NaviBarBtnRight titleText:nil image:@"navi_editor_nor"];
    
    //方式二
//    [self setupNaviBarWithTitle:@"自定义navbar"];
//    [self setupNaviBarWithBtn:NaviBarBtnLeft titleText:@"返回" image:@"navi_back_nor"];
//    [self setupNaviBarWithBtn:NaviBarBtnRight titleText:@"编辑" image:@"navi_editor_nor"];
    
    //设置默认空数据展示
    WEAKSELF(weakSelf);
    [self setUpShowExceptionViewWithImage:[UIImage imageNamed:@"toast_cry_4"] topGap:0 dealAction:^{
        [weakSelf setUpHiddenExceptionView];
        NSLog(@"被点击了");
    }];
}


- (void)handleBarBtnAction:(UIButton *)barbtn{
    if (barbtn.tag == NaviBarBtnLeft) {//也可默认不处理交给父类
        if ([[self.navigationController viewControllers] count] != 1) {
            [self.navigationController popViewControllerAnimated:YES];
        }

    }else if(barbtn.tag == NaviBarBtnRight){
        NSLog(@"点击了右侧的按钮");
    }
}
方法一
方法二

异常提示界面

好了到这里自定义NavigationBar基本就完成了

不过该方案没有解决以下问题

1 导航栏侧滑是的转场特效被去掉了
2 不能再创建UIViewcontroller的时候直接设置title

demo地址 CustomNavigationBar分支

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

推荐阅读更多精彩内容

  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,419评论 25 707
  • 回忆不起究竟什么时候开始喜欢末班车。一方面看着车上的乘客,猜想他们都会有什么样的理由要自己赶这班车;另一方...
    K派生活阅读 469评论 0 0
  • 开始读的有点散,不是很能进入状态。 第一天: 刚刚看了100页,目前就是讲青少年男女相处一些细腻的小心思。以前我可...
    A白衣素裳阅读 548评论 0 0
  • 我很庆幸生在一个和睦的家庭,年幼时与父母辗转各地工作,求学。每每来到一个陌生的地方,总能很快适应当地的氛围,与朋友...
    良人犹美人阅读 257评论 0 3