灵活轻量的自定义导航栏,导航栏属于控制器view,支持导航栏联动,一行代码实现【导航栏背景图片设置、导航栏渐变、折叠、修改Item大小和边距、自定义导航栏高度、全屏手势返回、pop拦截】等各种效果

github地址👉 ZXNavigationBar

安装

通过CocoaPods安装

pod 'ZXNavigationBar'

或手动导入

  • 将ZXNavigationBar拖入项目中。

导入头文件

#import "ZXNavigationBarController.h"

功能&特点

  • 每个控制器单独管理自己的导航栏,导航栏属于控制器的子view,不再属于统一的导航控制器
  • 兼容iOS8-iOS14,兼容刘海屏等各种设备,且无需担心系统更新需要重新适配导航栏
  • 灵活自定义导航栏背景图片、背景色、导航栏渐变等
  • 仅需一行代码即可轻松设置导航栏各种属性
  • 仅需一行代码即可实现拦截pop手势与点击返回事件,并决定是否要响应pop操作
  • 仅需一行代码即可兼容scrollView横向滚动
  • 支持随时切换为系统导航栏,且与系统导航栏之间无缝衔接
  • 支持自定义ZXNavigationBar高度
  • 支持在ZXNavigationBar上自定义titleView
  • 支持导航栏折叠、跟随ScrollView滚动透明度自动改变
  • 支持通过url加载导航栏Item
  • 支持全屏手势返回
  • 支持自定义手势返回范围
  • 支持监听手势返回进度
  • ZXNavigationBar自带效果都无法满足,支持任意自定义导航栏
  • 若从Xib中加载控制器View,添加子View无需手动设置距离导航栏顶部约束,ZXNavigationBar会自动处理

效果预览

导航栏设置 仿微博热搜效果 自定义导航栏
image
image
image
自定义titleView 兼容scrollView横向滚动 可伸缩式导航栏
image
image
image

开始使用

将控制器继承于ZXNavigationBarController,建议将Base控制器继承于ZXNavigationBarController

@interface DemoBaseViewController : ZXNavigationBarController

@end

【重要】注意事项

  • ZXNavigationBar会自动显示返回按钮,且实现点击pop功能,您无需设置,若需要自定义返回按钮,直接覆盖self.zx_navLeftBtn的图片和点击回调即可
  • 如果项目中存在黑白状态栏交替的需求,建议先在base控制器的viewDidLoad方法中统一设置状态栏颜色,以避免设置成白色状态栏后返回上一个页面无法自动恢复为黑色状态栏
@interface DemoBaseViewController : ZXNavigationBarController
- (void)viewDidLoad{
    [super viewDidLoad];
    self.zx_navStatusBarStyle = ZXNavStatusBarStyleDefault;
}
@end
  • ZXNavigationBarController作了自动隐藏导航栏的处理,但由于导航栏早于内部子控制器加载,因此有可能造成自定义导航栏抖动或状态栏颜色黑白相嵌的问题,
    若您遇到此问题,请在base导航控制器的pushViewController:animated:中设置self.navigationBarHidden = YES;或在Appdelegate的application:didFinishLaunchingWithOptions:中调用方法[UINavigationController zx_hideAllNavBar]【需要先#import "ZXNavigationBarController.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    //这个方法需要在导航控制器加载前调用
    [UINavigationController zx_hideAllNavBar];
    
    UIWindow *window = [[UIWindow alloc]initWithFrame:[UIScreen mainScreen].bounds];
    DemoListViewController *vc = [[DemoListViewController alloc]init];
    UINavigationController *nav = [[UINavigationController alloc]initWithRootViewController:vc];
    window.rootViewController = nav;
    [window makeKeyAndVisible];
    self.window = window;
    return YES;
}
@end

或将您的导航控制器继承于ZXNavigationBarNavigationController或使用ZXNavigationBarNavigationController作为您的导航控制器

【重要】ZXNavigationBar对于自定义导航栏view内容无法自动下移的处理方式

如果App使用的是系统的导航栏且不透明,view的内容会自动下移,例如非刘海屏会下移64像素;若设置了自定义的导航栏,因为它实际上就是普通的View,则控制器view中的内容不会自动下移以避免挡住导航栏。

  • ZXNavigationBar的处理方法是:
  • 如果您是通过frame或者Masonry设置控件布局,请设置y距离顶部高度为导航栏高度,可直接使用ZXNavBarHeight这个宏
  • 如果您是通过Xib加载控制器View,则ZXNavigationBar会自动将内部约束设置为距离顶部为导航栏高度+原始高度,您无需作任何处理
  • 若您是通过Xib加载控制器View,且禁用了SafeArea,请设置(若使用了SafeArea,请忽略):
//若大多数控制器都从Xib加载并禁用了SafeArea,可以直接在Base控制器中设置
self.zx_isEnableSafeArea = NO;

导航栏设置

注意:以下设置均在控制器中进行,self代表当前控制器(当前控制器需继承于ZXNavigationBarController)

设置导航栏标题

self.title = @"ZXNavigationBar";

self.zx_navTitle = @"ZXNavigationBar";

设置导航栏标题颜色

self.zx_navTitleColor = [UIColor redColor];

设置导航栏标题字体大小

self.zx_navTitleFontSize = 20;

self.zx_navTitleFont = [UIFont systemFontOfSize:20];
  • 设置导航栏标题其他非frame属性,通过控制self.zx_navTitleLabel即可

快速设置左侧/右侧的按钮和点击回调(以右侧按钮为例)

  • 设置最右侧按钮的图片名和点击回调
[self zx_setRightBtnWithImgName:@"set_icon" clickedBlock:^(UIButton * _Nonnull btn) {
    NSLog(@"点击了最右侧的Button");
}];
  • 设置最右侧按钮的文字和点击回调
[self zx_setRightBtnWithText:@"右侧按钮" clickedBlock:^(UIButton * _Nonnull btn) {
    NSLog(@"点击了最右侧的Button");
}];
  • 设置最右侧按钮的图片对象和点击回调
[self zx_setLeftBtnWithImg:image对象 clickedBlock:^(ZXNavItemBtn * _Nonnull btn) {
    NSLog(@"点击了最右侧的Button");  
}];
  • 设置最右侧按钮的图片Url和点击回调(需导入SDWebImage并在pch文件中#import <SDWebImage/UIButton+WebCache.h>)
[self zx_setLeftBtnWithImgUrl:@"图片url地址" placeholderImgName:@"占位图名称" clickedBlock:^(ZXNavItemBtn * _Nonnull btn) {
    NSLog(@"点击了最右侧的Button");  
}];

根据左侧/右侧的按钮对象进行具体设置(以左侧按钮为例)

  • 如果需要导航栏显示返回图标和返回文字
//由于ZXNavigationBar会自动在左侧添加返回图片和点击返回事件,因此只需要设置返回的文字即可
[self.zx_navLeftBtn setTitle:@"返回" forState:UIControlStateNormal];
  • 设置导航栏左侧/右侧的按钮的其他非frame相关属性,通过控制self.zx_navLeftBtn/zx_navRightBtn即可

将图片渲染为指定颜色

//将oldImage渲染为红色
UIImage *resultImage = [oldImage zx_renderingColor:[UIColor redColor]];

设置左侧/右侧的按钮的大小

//将按钮宽高设置为30
self.zx_navItemSize = 30;

设置左侧/右侧的按钮的间距与边距

//将按钮边距设置为0
self.zx_navItemMargin = 0;

设置右侧第二个按钮

  • 将上述例子中zx_navLeftBtn/zx_navRightBtn修改为zx_navSubRightBtn即可

设置导航栏背景颜色

self.zx_navBarBackgroundColor = [UIColor orangeColor];

设置导航栏背景图片

self.zx_navBarBackgroundImage = [UIImage imageNamed:@"nav_bac"];

设置导航栏渐变背景

//从magentaColor到cyanColor渐变
[self zx_setNavGradientBacFrom:[UIColor magentaColor] to:[UIColor cyanColor]];

移除导航栏渐变背景

[self zx_removeNavGradientBac];

设置导航栏TintColor(此属性可以将导航栏的title颜色、左右Button的文字和图片颜色修改为TintColor)

self.zx_navTintColor = [UIColor yellowColor];

自定义导航栏高度(若设置此属性,则ZXNavigationBar不会再使用默认的导航栏高度)

self.zx_navFixHeight = 30;

设置导航栏大小标题效果

[self zx_setMultiTitle:@"ZXNavigationBar" subTitle:@"subTitle"];

[self zx_setMultiTitle:@"ZXNavigationBar" subTitle:@"subTitle" subTitleFont:[UIFont systemFontOfSize:10] subTitleTextColor:[UIColor redColor]];

设置分割线背景颜色

self.zx_navLineViewBackgroundColor = [UIColor blueColor];
  • 分割线其他其他非frame相关属性通过self.zx_navLineView设置即可

设置伸缩式导航栏

  • 如果设置了控制器的Xib且在Xib中设置了子视图的约束(仅需设置展开或者折叠导航栏与动画效果速度,无需手动调整控制器View子视图的frame)
//第一个参数folded:控制是展开还是折叠导航栏;第二个参数speed:控制展开或收缩导航栏的速度,0-6,建议值为3;第三个参数offsetBlock:折叠动画导航栏位移回调;第四个参数completionBlock:折叠动画结束回调
[self zx_setNavFolded:YES speed:3 foldingOffsetBlock:nil foldCompletionBlock:nil];
  • 如果是通过Frame设置控制器View的子视图(如TableView),需要在foldingOffsetBlock回调中控制导航栏下方View的frame,使其始终紧贴导航栏底部
__weak typeof(self) weakSelf = self;
[self zx_setNavFolded:shouldFold speed:3 foldingOffsetBlock:^(CGFloat offset) {
    //tableView的y值跟随这导航栏变化(导航栏高度减小,tableView的y值减小)
    weakSelf.tableView.y += offset;
    //tableView的高度值跟随这导航栏变化(导航栏高度减小,tableView高度增加)
    weakSelf.tableView.height -= offset;
} 

通过ScrollView滚动自动控制导航栏透明效果(仿微博热搜效果)

  • 在页面加载的时候,需要先设置导航栏透明(注意,不要直接设置导航栏的alpha或设置导航栏的背景色为[UIColor clearColor]),请使用
//这一行代码实际上是把导航栏背景色的透明度改为0,仅改变RGBA中A(alpha)的值,如果导航栏有自定义背景色,则会从透明-自定义背景色过渡
self.zx_navBarBackgroundColorAlpha = 0;
  • 在scrollViewDidScroll中
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    //scrollView:滚动控制的scrollView,tableView或collectionView
    //fullChangeHeight:scrollView.contentOffset.y达到fullChangeHeight时,导航栏变为完全不透明
    //changeLimitNavAlphe:当导航栏透明度达到changeLimitNavAlphe时,将触发opaqueBlock,通知控制器设置导航栏不透明时的效果
    //transparentBlock:导航栏切换到透明状态时的回调(默认透明度0.7为临界点)
    //opaqueBlock:导航栏切换到不透明状态时的回调(默认透明度0.7为临界点)
    [self zx_setNavTransparentGradientsWithScrollView:scrollView fullChangeHeight:100 changeLimitNavAlphe:0.7 transparentGradientsTransparentBlock:^{
        //导航栏透明时的额外效果设置
    } transparentGradientsOpaqueBlock:^{
        //导航栏不透明时的额外效果设置
    }];
}
  • 若需要复杂的自定义场景(例如导航栏有背景图片,因默认处理方式是通过控制导航栏背景颜色的透明度实现,因此此时需要监听导航栏透明度改变回调,并写上self.zx_navBacImageView.alpha = alpha;
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView{
    //scrollView:滚动控制的scrollView,tableView或collectionView
    //fullChangeHeight:scrollView.contentOffset.y达到fullChangeHeight时,导航栏变为完全不透明
    //changeLimitNavAlphe:当导航栏透明度达到changeLimitNavAlphe时,将触发opaqueBlock,通知控制器设置导航栏不透明时的效果
    //transparentBlock:导航栏切换到透明状态时的回调(默认透明度0.7为临界点)
    //opaqueBlock:导航栏切换到不透明状态时的回调(默认透明度0.7为临界点)
    [self zx_setNavTransparentGradientsWithScrollView:scrollView fullChangeHeight:100 changeLimitNavAlphe:0.7 transparentGradientsChangingBlock:^(CGFloat alpha) {
        //导航栏透明度正在改变时候处理
    } transparentGradientsTransparentBlock:^{
        //导航栏透明时的额外效果设置
    } transparentGradientsOpaqueBlock:^{
        //导航栏不透明时的额外效果设置
    }];
}

设置状态栏为白色

self.zx_navStatusBarStyle = ZXNavStatusBarStyleLight;

是否禁止根据zx_navStatusBarStyle自动调整状态栏颜色,默认为否

self.zx_disableAutoSetStatusBarStyle = YES;

设置状态栏为黑色

self.zx_navStatusBarStyle = ZXNavStatusBarStyleDefault;

显示系统导航栏(默认为否)

//显示系统导航栏将会自动隐藏ZXNavigationBar
self.zx_showSystemNavBar = YES;

隐藏ZXNavigationBar(默认为否)

//显示系统导航栏将会自动隐藏ZXNavigationBar
self.zx_hideBaseNavBar = YES;

自定义导航栏与系统导航栏平滑过渡

//务必仅当存在系统导航栏与自定义导航栏过渡时启用,非必要请勿启用,否则可能造成自定义导航栏跳动,若当前控制器显示了系统导航栏,请于当前控制器pop的上一个控制器中使用self.zx_navEnableSmoothFromSystemNavBar = YES)
self.zx_navEnableSmoothFromSystemNavBar = YES;
  • 若需要更优的平滑过渡效果,请将您的导航控制器继承于ZXNavigationBarNavigationController或使用ZXNavigationBarNavigationController作为您的导航控制器

禁止Xib加载控制器情况下自动将顶部View约束下移导航栏高度(默认为否)

self.zx_disableNavAutoSafeLayout = YES;

自动将顶部View约束下移导航栏高度时的回调,可拦截并自定义下移距离(从xib加载控制器view时生效)

//oldNavOffset:视图在xib中所设置的约束与顶部距离
//currentNavOffset:即将设置的视图与顶部的距离
//返回值为当前需要自定义设置的视图与控制器顶部的距离
self.zx_handleAdjustNavContainerOffsetBlock = ^CGFloat(CGFloat oldNavOffset, CGFloat currentNavOffset) {
    //此设置代表从xib加载控制器view时,控制器view中的内容距离控制器顶部10像素(默认距离顶部为导航栏高度)
    return 10;
};

自定义TitleView

//创建自定义View
UIView *customTitleView = [[UIView alloc]init];
[self zx_addCustomTitleView:customTitleView];

自定义导航栏View

//创建自定义View
UIView *customNav = [[UIView alloc]init];
[self zx_addCustomNavBar:customNav];

拦截侧滑返回手势和返回按钮点击事件

//拦截侧滑返回手势和返回按钮点击事件
self.zx_handlePopBlock = ^BOOL(ZXNavigationBarController * _Nonnull viewController, ZXNavPopBlockFrom popBlockFrom) {
    //viewController:当前控制器
    //popBlockFrom:通过什么方式(点击返回按钮或侧滑返回手势)触发pop操作
    
    //doSomething
    //返回YES则代表不禁止pop操作,返回NO则禁止pop操作
    return YES;
};

设置全屏返回手势

将您的导航控制器继承于`ZXNavigationBarNavigationController`或使用`ZXNavigationBarNavigationController`作为您的导航控制器即可

设置全屏返回手势响应范围

将您的导航控制器继承于ZXNavigationBarNavigationController或使用ZXNavigationBarNavigationController作为您的导航控制器

//在控制器中:
//pop手势的触发范围比例,0-1,默认为1,即代表全屏触发
self.zx_popGestureCoverRatio = 0.5;

禁用全屏pop手势,若禁用,则pop触发范围为屏幕宽度的五分之一

//在控制器中:
self.zx_disableFullScreenGesture = YES;

监听全屏返回手势进度

将您的导航控制器继承于ZXNavigationBarNavigationController或使用ZXNavigationBarNavigationController作为您的导航控制器

//在控制器中:
self.zx_handleCustomPopGesture = ^(CGFloat popOffsetProgress) {
    NSLog(@"popOffsetProgress--%lf",popOffsetProgress);
};

兼容pop返回手势与scrollView横向滚动手势

//在控制器中:
[self zx_setPopGestureCompatibleScrollView:self.scrollView];

兼容pop返回手势与scrollView横向滚动手势(自定义处理)

当导航控制器为ZXNavigationBarNavigationController或继承于ZXNavigationBarNavigationController时,如果需要更复杂的定制化的情况,可以使用下方的方式,以下代码的作用是在scrollView滚动到第一页的时候,支持pop手势多层级同时触发,为了更好的展示效果,建议关闭scrollView的bounces。此段代码与上方效果一致

//在控制器中:
self.scrollView.bounces = NO;
__weak typeof(self) weakSelf = self;
self.zx_popGestureShouldRecognizeSimultaneously = ^BOOL(UIGestureRecognizer * _Nonnull otherGestureRecognizer) {
    if(weakSelf.scrollView.contentOffset.x < weakSelf.scrollView.width){
        return YES;
    }
    return NO;
};

push自动隐藏tabbar

将您的导航控制器继承于ZXNavigationBarNavigationController或使用ZXNavigationBarNavigationController作为您的导航控制器即可
若您要禁用这一功能

//在控制器中:
self.navigationController.zx_disableAutoHidesBottomBarWhenPushed = YES;

版本记录,请查阅Release


更多示例,可下载Demo查阅,若有任何问题,可随时在issue中提出

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