iOS-自定义导航条

作者: 路飞_Luck | 来源:发表于2018-10-20 18:21 被阅读273次
    前言

    随着项目功能的需要,可能有些界面的导航条比较特殊,比如

    • 需要添加不同的按钮,比如扫码,心愿单,分享,更多等等。
    • 需要将菜单替换成图片
    • 需要隐藏整个导航条
    • 需要随着滚动,导航条背景跟着变色
    • 隐藏导航条底部的线条
    • ...
      所以自定义一个导航条就显得至关重要了。
    效果图
    addGes.gif
    实现思路
    • 1.自定义一个导航条视图 NaviBarView,并且对外提供各种方法可以动态修改要展示的内容。
    • 2.定义一个 VC 的基类,项目中所有的 VC 都继承它。在它里面创建并且维护一个导航条,隐藏系统自带的导航条,并且对外提供各种方法。

    部分相关代码如下

    1.NaviBarView类的实现
    • NaviBarView.h
    #import <UIKit/UIKit.h>
    @class BaseViewController;
    
    #define StatusBarHeight [[UIApplication sharedApplication] statusBarFrame].size.height
    #define NavBarHeight 44
    #define NavHeight (StatusBarHeight + NavBarHeight)
    #define ScreenWidth [UIScreen mainScreen].bounds.size.width
    #define ScreenHeight [UIScreen mainScreen].bounds.size.height
    
    /**
     导航条
     */
    @interface NaviBarView : UIView
    
    @property (retain, nonatomic) UIView *statusBar;    // 状态栏
    @property (retain, nonatomic) UIView *navigationBar;    // 导航条
    @property (retain, nonatomic) UIButton *rightWishBtn;   // 右侧分享按钮
    @property (retain, nonatomic) UIButton *leftMenuBtn;    // 左侧菜单栏
    @property (retain, nonatomic) UIButton *backBtn;    // 返回按钮
    @property (retain, nonatomic) UILabel *titleLabel;  // 标题
    @property(nonatomic, strong)UIView *lineView;   // 底部分割线
    
    - (instancetype)initWithController:(BaseViewController *)controller;
    
    // 扫码和心愿单
    - (void)addScanAndWishList;
    // 添加返回按钮
    - (void)addBackBtn;
    // 添加底部分割线
    - (void)addBottomSepLine;
    // 设置标题
    - (void)setNavigationTitle:(NSString *)title;
    // 设置导航条透明
    - (void)clearNavBarBackgroundColor;
    
    // 右侧添加按钮
    - (UILabel *)addRightMenu:(NSString *)actionName withAction:(SEL)action;
    
    @end
    
    • NaviBarView.m初始化
    // 初始化
    - (instancetype)initWithController:(BaseViewController *)controller {
        _controller = controller;
        
        self = [super initWithFrame:CGRectMake(0, 0, ScreenWidth, NavHeight)];
        self.backgroundColor = [UIColor clearColor];
        self.layer.zPosition = 999999;
        
        // 最顶部的状态栏
        _statusBar = [[UIView alloc] initWithFrame:CGRectMake(0, 0, ScreenWidth, StatusBarHeight)];
        _statusBar.backgroundColor = [UIColor whiteColor];
        [self addSubview:_statusBar];
        
        _navigationBar = [[UIView alloc] initWithFrame:CGRectMake(0, StatusBarHeight, ScreenWidth, NavBarHeight)];
        _navigationBar.backgroundColor = [UIColor whiteColor];
        [self addSubview:_navigationBar];
    
        return self;
    }
    
    • NavBarView.m添加视图 - 只罗列部分代码
    // 返回按钮
    - (void)addBackBtn {
        // 不能添加多个
        UIView *srcBack = [_navigationBar viewWithTag:TagBackBtn];
        if (srcBack)
            return;
        
        UIImage *background = [[UIImage imageNamed:@"nav_back_black"] ifRTLThenOrientationUpMirrored];
        UIImage *backgroundOn = [[UIImage imageNamed:@"nav_back_black"] ifRTLThenOrientationUpMirrored];
        
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        [button setAccessibilityIdentifier:@"TopBackBtn"];
        button.tag = TagBackBtn;
        [button onTap:self action:@selector(doBackPrev)];
        
        [button setImage:background forState:UIControlStateNormal];
        [button setImage:backgroundOn forState:UIControlStateHighlighted];
        
        button.frame = CGRectMake(0, 0, 60, 44);
        button.contentEdgeInsets = UIEdgeInsetsMake(7, 7, 7, 20);
        [_navigationBar addSubview:button];
        [button rtlToParent];
        _backBtn = button;
    }
    
    // 添加顶部右边的文字类的按钮
    - (UILabel *)addRightMenu:(NSString *)actionName withAction:(SEL)action {
        
        // 当重复添加时,移除旧的按钮
        UILabel *srcLabel = (UILabel *)[_navigationBar viewWithTag:TagRightMenu];
        if (srcLabel) {
            [srcLabel removeFromSuperview];
        }
        
        UILabel *lb = [[UILabel alloc] initWithFrame:CGRectMake(ScreenWidth - 110, 0, 100, 44)];
        lb.backgroundColor = [UIColor clearColor];
        lb.font = [UIFont systemFontOfSize:16];
        lb.textColor = [UIColor blackColor];
        lb.text = actionName;
        lb.userInteractionEnabled = YES;
        lb.textAlignment = NSTextAlignmentRight;
        lb.tag = TagRightMenu;
        [lb onTap:_controller action:action];
        [_navigationBar addSubview:lb];
        [lb rtlToParent];
        _rightMenuView = lb;
        return lb;
    }
    
    • NaviBarView.m - 一些设置方法
    #pragma mark - set
    
    - (void)clearNavBarBackgroundColor {
        _statusBar.backgroundColor = [UIColor clearColor];
        _navigationBar.backgroundColor = [UIColor clearColor];
        _titleLabel.textColor = [UIColor whiteColor];
        [_navigationBar removeChildByTag:kTagLine];
    }
    
    - (void)setNavigationTitle:(NSString *)title {
        self.titleLabel.text = title;
    }
    
    - (void)setBackImage:(NSString *)imageName {
        UIImage *background = [UIImage imageNamed:imageName];
        UIImage *backgroundOn = [UIImage imageNamed:imageName];
        [_backBtn setImage:background forState:UIControlStateNormal];
        [_backBtn setImage:backgroundOn forState:UIControlStateHighlighted];
    }
    

    ####### 2.BaseViewController基类的实现

    • BaseViewController.h部分代码
    /**
     基类
     */
    @interface BaseViewController : UIViewController
    
    #pragma mark - 导航条相关
    @property(nonatomic, copy)NSString *naviTitle;  // 标题
    /** 导航条 */
    @property(nonatomic, strong)NaviBarView *topNavBar;
    /** 内容视图 */
    @property (strong, nonatomic) UIView *containerView;
    
    // 返回按钮点击操作
    - (void)doBackPrev;
    // 扫码和心愿单
    - (void)addScanAndWishList;
    // 设置导航条透明
    - (void)clearNavBarBackgroundColor;
    
    // 添加按钮
    - (UIButton *)addBtnWithTitle:(NSString *)title action:(SEL)action;
    
    - (UILabel *)addRightMenu:(NSString *)actionName withAction:(SEL)action;
    
    @end
    
    • BaseViewController.m实现
    @implementation BaseViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // setup data
        self.view.backgroundColor = [UIColor whiteColor];
        // 所有界面隐藏导航栏,用自定义的导航栏代替
        self.fd_prefersNavigationBarHidden = YES;
        // drawUI
        [self drawTopNaviBar];
    }
    @end
    

    为什么要设置fd_prefersNavigationBarHidden参数为 YES,因为本项目是使用FDFullscreenPopGesture来管理导航条,隐藏系统的导航条已经被它所实现了。

    - (void)fd_viewWillAppear:(BOOL)animated
    {
        // Forward to primary implementation.
        [self fd_viewWillAppear:animated];
        
        if (self.fd_willAppearInjectBlock) {
            self.fd_willAppearInjectBlock(self, animated);
        }
        
        //设置导航的显示/隐藏
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(CGFLOAT_MIN * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            UIViewController *vc = [self.navigationController.viewControllers lastObject];
            if (vc.fd_prefersNavigationBarHidden) {
                [self.navigationController setNavigationBarHidden:YES animated:NO];
            } else {
                [self.navigationController setNavigationBarHidden:NO animated:NO];
            }
        });
    }
    
    • 在界面即将显示的时候,将导航条推至最前面,避免被其他视图遮挡
    - (void)viewWillAppear:(BOOL)animated {
        [super viewWillAppear:animated];
        if (self.navigationController.navigationBar.height == 0) {
            _topNavBar.alpha = 0;
        }
        // 将导航放到最顶部,不然后面有其它的层会被覆盖
        [self.view bringSubviewToFront:_topNavBar];
    }
    
    • 导航条的绘制
    /// 在旋转界面时重新构造导航条
    - (void)drawTopNaviBar {
        if (_topNavBar) {
            [_topNavBar removeFromSuperview];
        }
        // 添加自定义的导航条
        NaviBarView *naviBar = [[NaviBarView alloc] initWithController:self];
        [self.view addSubview:naviBar];
        self.topNavBar = naviBar;
        
        if (![self isKindOfClass:[HomeViewController class]] && ![self isKindOfClass:[AccountViewController class]]) {
            // 添加返回按钮
            [_topNavBar addBackBtn];
            // 添加底部分割线 - 如果不需要添加,这里处理即可
            [_topNavBar addBottomSepLine];
        }
        
        // 自动放一个容器在下面,如果全屏的界面就往 self.view 加子,非全屏的往 container 加
        self.containerView = [[UIView alloc] initWithFrame:CGRectMake(0, NavHeight, ScreenWidth, ScreenHeight - NavHeight)];
        self.containerView.backgroundColor = [UIColor whiteColor];
        [self.view addSubview:self.containerView];
    }
    
    • 还有其他代码请下载本文的项目即可
    3.使用
    • 新建的 VC 必须继承自BaseViewController才可以使用
    3.1 导航条添加扫码,搜索,心愿单功能
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        // 添加扫码,搜索,心愿单
        [self addScanAndWishList];
    }
    
    image.png
    3.2 导航条正常样式+右侧添加分享图标
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        self.naviTitle = @"First VC";
        
        [self addRightMenu:@"分享" withAction:@selector(tapShare)];
    }
    
    image.png
    3.3 导航条只有返回按钮,没有标题,没有底部线条
    - (void)viewDidLoad {
        [super viewDidLoad];
        // Do any additional setup after loading the view.
        
        self.naviTitle = @"隐藏导航条分割线";
        [self clearNavBarBackgroundColor];
    }
    
    image.png

    更多功能,只需要根据需要,自定义导航条视图类NaviBarView即可。


    • 如有错误,欢迎指正,多多点赞,打赏更佳,您的支持是我写作的动力。

    项目连接地址 - FDFullScreenPopGestureDemo

    相关文章

      网友评论

      • kakukeme:试了下,横竖屏切换时,没有更新;

      本文标题:iOS-自定义导航条

      本文链接:https://www.haomeiwen.com/subject/gdpmzftx.html