前言
随着项目功能的需要,可能有些界面的导航条比较特殊,比如
- 需要添加不同的按钮,比如扫码,心愿单,分享,更多等等。
- 需要将菜单替换成图片
- 需要隐藏整个导航条
- 需要随着滚动,导航条背景跟着变色
- 隐藏导航条底部的线条
- ...
所以自定义一个导航条就显得至关重要了。
效果图
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
即可。
- 如有错误,欢迎指正,多多点赞,打赏更佳,您的支持是我写作的动力。
网友评论