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(@"点击了右侧的按钮");
    }
}
iOS 一行代码自定义带导航栏的UIViewcontroller_第1张图片
方法一
iOS 一行代码自定义带导航栏的UIViewcontroller_第2张图片
方法二

iOS 一行代码自定义带导航栏的UIViewcontroller_第3张图片
异常提示界面

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

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

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

demo地址 CustomNavigationBar分支

你可能感兴趣的:(iOS 一行代码自定义带导航栏的UIViewcontroller)