灵活轻量的自定义导航栏,导航栏属于控制器view,支持导航栏联动,一行代码实现【导航栏背景图片设置、导航栏渐变、折叠、修改Item大小和边距、自定义导航栏高度、全屏手势返回、pop拦截、仿系统导航栏历史堆栈】等各种效果
pod 'ZXNavigationBar'
#import "ZXNavigationBarController.h"
ZXNavigationBar
高度ZXNavigationBar
上自定义titleView与navItemViewZXNavigationBar
自带效果都无法满足,支持任意自定义导航栏ViewZXNavigationBar
会自动处理导航栏设置 | 仿微博热搜效果 | 自定义导航栏 |
---|---|---|
自定义titleView | 兼容scrollView横向滚动 | 可伸缩式导航栏 |
---|---|---|
ZXNavigationBarController
,建议将Base控制器继承于ZXNavigationBarController
@interface DemoBaseViewController : ZXNavigationBarController
@end
ZXNavigationBarNavigationController
,建议将Base导航控制器继承于ZXNavigationBarNavigationController
@interface DemoBaseNavigationController : ZXNavigationBarNavigationController
@end
ZXNavigationBar
会自动显示返回按钮,且实现点击pop功能,您无需设置,若需要自定义返回按钮,直接覆盖self.zx_navLeftBtn
的图片和点击回调即可viewDidLoad
方法中统一设置状态栏颜色,以避免设置成白色状态栏后返回上一个页面无法自动恢复为黑色状态栏@interface DemoBaseViewController : ZXNavigationBarController
- (void)viewDidLoad{
[super viewDidLoad];
self.zx_navStatusBarStyle = ZXNavStatusBarStyleDefault;
}
@end
UIScrollView.appearance.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentAutomatic;
ZXNavigationBarNavigationController
或其子类时,可忽略此步操作!!】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
FDFullscreenPopGesture
ZXNavigationBar
支持全屏手势返回和拦截,建议您移除其他的第三方全屏手势返回框架以避免冲突。ZXNavigationBarController
的跟控制器的初始化方法书写以下代码以兼容FDFullscreenPopGesture
://因FDFullscreenPopGesture默认会在控制器即将展示时显示系统导航栏,与ZXNavigationBar共同使用时会造成系统导航栏出现一下又马上消失,因此需要以下设置
self.fd_prefersNavigationBarHidden = YES;
//当您通过zx_handlePopBlock拦截侧滑返回手势时,请设置fd_interactivePopDisabled为YES以关闭FDFullscreenPopGesture在当前控制器的全屏返回手势,否则无法拦截。
self.fd_interactivePopDisabled = YES;
ZXNavigationBar
对于自定义导航栏view内容无法自动下移的处理方式如果App使用的是系统的导航栏且不透明,view的内容会自动下移,例如非刘海屏会下移64像素;若设置了自定义的导航栏,因为它实际上就是普通的View,则控制器view中的内容不会自动下移以避免挡住导航栏。
ZXNavigationBar
的处理方法是:ZXNavBarHeight
这个宏ZXNavigationBar
会自动将内部约束设置为距离顶部为导航栏高度+原始高度,您无需作任何处理ZXNavigationBarController
)self.title = @"ZXNavigationBar";
或
self.zx_navTitle = @"ZXNavigationBar";
self.zx_navTitleColor = [UIColor redColor];
self.zx_navTitleFontSize = 20;
或
self.zx_navTitleFont = [UIFont systemFontOfSize:20];
self.zx_navTitleLabel
即可self.zx_navFixFrame = CGRectMake(0, 0, 100, 100);
self.zx_navHandleFrameBlock = ^CGRect(CGRect oldFrame) {
return CGRectMake(0, 0, 100, 100);
};
self.zx_backBtnImageName = @"newBackImageName";
[self zx_setRightBtnWithImgName:@"set_icon" clickedBlock:^(UIButton * _Nonnull btn) {
NSLog(@"点击了最右侧的Button");
}];
[self zx_setRightBtnWithText:@"右侧按钮" clickedBlock:^(UIButton * _Nonnull btn) {
NSLog(@"点击了最右侧的Button");
}];
[self zx_setRightBtnWithImg:image对象 clickedBlock:^(ZXNavItemBtn * _Nonnull btn) {
NSLog(@"点击了最右侧的Button");
}];
[self zx_setRightBtnWithImgUrl:@"图片url地址" placeholderImgName:@"占位图名称" clickedBlock:^(ZXNavItemBtn * _Nonnull btn) {
NSLog(@"点击了最右侧的Button");
}];
//由于ZXNavigationBar会自动在左侧添加返回图片和点击返回事件,因此只需要设置返回的文字即可
[self.zx_navLeftBtn setTitle:@"返回" forState:UIControlStateNormal];
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
即可zx_navLeftBtn
/zx_navRightBtn
修改为zx_navSubLeftBtn
即可ZXNavItemBtn
的属性(ZXNavItemBtn
在导航栏中从左到右分别为zx_navLeftBtn
,zx_navSubLeftBtn
,zx_navSubRightBtn
,zx_navRightBtn
),以下以zx_navLeftBtn
为例:self.zx_navLeftBtn.zx_imageColor = [UIColor redColor];
self.zx_navLeftBtn.zx_tintColor = [UIColor redColor];
self.zx_navLeftBtn.zx_fontSize = 12;
self.zx_navLeftBtn.zx_fixWidth = 100;
zx_fixWidth
为-1)self.zx_navLeftBtn.zx_fixHeight = 100;
self.zx_navLeftBtn.zx_fixMarginLeft = 15;
self.zx_navLeftBtn.zx_fixMarginRight = 15;
self.zx_navLeftBtn.zx_disableAutoLayoutImageAndTitle = YES;
self.zx_navLeftBtn.zx_fixImageSize = CGSizeMake(10,10);
self.zx_navLeftBtn.zx_textAttachWidth = 20;
self.zx_navLeftBtn.zx_textAttachHeight = 10;
self.zx_navLeftBtn.zx_setCornerRadiusRounded = YES;
self.zx_navLeftBtn.zx_imageOffsetX = -10;
self.zx_navLeftBtn.zx_useTintColorOnlyInStateNormal = YES;
self.zx_navLeftBtn.zx_customView = [UISwitch new];
self.zx_navLeftBtn.zx_handleFrameBlock = ^CGRect(CGRect oldFrame) {
return CGRectMake(oldFrame.origin.x, oldFrame.origin.y, oldFrame.size.width + 10, 30);
};
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];
self.zx_navTintColor = [UIColor yellowColor];
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];
self.zx_navLineViewHeight = 2;
self.zx_navLineView
设置即可self.zx_navStatusBarStyle = ZXNavStatusBarStyleLight;
self.zx_navStatusBarStyle = ZXNavStatusBarStyleDefault;
self.zx_disableAutoSetStatusBarStyle = YES;
//显示系统导航栏将会自动隐藏ZXNavigationBar
self.zx_showSystemNavBar = YES;
self.zx_hideBaseNavBar = YES;
//务必仅当存在系统导航栏与自定义导航栏过渡时启用,非必要请勿启用,否则可能造成自定义导航栏跳动,若当前控制器显示了系统导航栏,请于当前控制器pop的上一个控制器中使用self.zx_navEnableSmoothFromSystemNavBar = YES)
self.zx_navEnableSmoothFromSystemNavBar = YES;
ZXNavigationBarNavigationController
或使用ZXNavigationBarNavigationController
作为您的导航控制器self.zx_disableNavAutoSafeLayout = YES;
self.zx_enableAdjustNavContainerAll = YES;
//oldNavOffset:视图在xib中所设置的约束与顶部距离
//currentNavOffset:即将设置的视图与顶部的距离
//返回值为当前需要自定义设置的视图与控制器顶部的距离
self.zx_handleAdjustNavContainerOffsetBlock = ^CGFloat(CGFloat oldNavOffset, CGFloat currentNavOffset) {
//此设置代表从xib加载控制器view时,控制器view中的内容距离控制器顶部10像素(默认距离顶部为导航栏高度)
return 10;
};
self.zx_showNavHistoryStackContentView = YES;
self.zx_navHistoryStackContentViewOffsetX = 10;
self.zx_navHistoryStackContentViewItemMaxLength = 10;
self.zx_navHistoryStackViewStyle = ZXNavHistoryStackViewStyleDark;
//以zx_navLeftBtn为例
self.zx_navLeftBtn.zx_customView = [UISwitch new];
//创建自定义View
UIView *customTitleView = [[UIView alloc]init];
[self zx_addCustomTitleView:customTitleView];
//创建自定义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;
};
//如果您在项目中使用了FDFullscreenPopGesture,需要关闭FDFullscreenPopGesture在当前控制器的全屏侧滑返回手势,否则zx_handlePopBlock无法拦截侧滑返回手势
self.fd_interactivePopDisabled = YES;
self.zx_navHandlePopGestureBlock = ^BOOL(UIViewController * _Nonnull topViewController) {
//这里的优先级高于控制器中的self.zx_handlePopBlock。如果返回NO,则代表直接关闭侧滑返回手势,控制器中的self.zx_handlePopBlock不会触发;如果返回YES,则交由控制器处理
return NO;
};
将您的导航控制器继承于`ZXNavigationBarNavigationController`或使用`ZXNavigationBarNavigationController`作为您的导航控制器即可
将您的导航控制器继承于ZXNavigationBarNavigationController
或使用ZXNavigationBarNavigationController
作为您的导航控制器
注意:因设置全屏返回手势响应范围
与禁用全屏pop手势
属于同一导航控制器,为避免此属性被其他子控制器修改,以下代码建议写在子控制器的-viewWillAppear
或-viewDidAppear
中
//在控制器中:
//pop手势的触发范围比例,0-1,默认为1,即代表全屏触发
self.zx_popGestureCoverRatio = 0.5;
禁用全屏pop手势,若禁用,则pop触发范围为屏幕宽度的十分之一
//在控制器中:
self.zx_disableFullScreenGesture = YES;
将您的导航控制器继承于ZXNavigationBarNavigationController
或使用ZXNavigationBarNavigationController
作为您的导航控制器
注意:因手势进度监听的block
属于同一导航控制器,为避免block被子控制器覆盖后失效,以下代码建议写在子控制器的-viewWillAppear
或-viewDidAppear
中
//在控制器中:
self.zx_handleCustomPopGesture = ^(CGFloat popOffsetProgress) {
NSLog(@"popOffsetProgress--%lf",popOffsetProgress);
};
将您的导航控制器继承于ZXNavigationBarNavigationController
或使用ZXNavigationBarNavigationController
作为您的导航控制器即可
若您要禁用这一功能
//在控制器中:
self.navigationController.zx_disableAutoHidesBottomBarWhenPushed = YES;
注意:因判断是否支持多层级的手势同时触发的block
属于同一导航控制器,为避免block被子控制器覆盖后失效,以下代码建议写在子控制器的-viewWillAppear
或-viewDidAppear
中
//在控制器中:
[self zx_setPopGestureCompatibleScrollView:self.scrollView];
当导航控制器为ZXNavigationBarNavigationController
或继承于ZXNavigationBarNavigationController
时,如果需要更复杂的定制化的情况,可以使用下方的方式,以下代码的作用是在scrollView滚动到第一页的时候,支持pop手势多层级同时触发,为了更好的展示效果,建议关闭scrollView的bounces。此段代码与上方效果一致
注意:因判断是否支持多层级的手势同时触发的block
属于同一导航控制器,为避免block被子控制器覆盖后失效,以下代码建议写在子控制器的-viewWillAppear
或-viewDidAppear
中
//在控制器中:
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;
};
//第一个参数folded:控制是展开还是折叠导航栏;第二个参数speed:控制展开或收缩导航栏的速度,0-6,建议值为3;第三个参数offsetBlock:折叠动画导航栏位移回调;第四个参数completionBlock:折叠动画结束回调
[self zx_setNavFolded:YES speed:3 foldingOffsetBlock:nil foldCompletionBlock:nil];
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;
}
//这一行代码实际上是把导航栏背景色的透明度改为0,仅改变RGBA中A(alpha)的值,如果导航栏有自定义背景色,则会从透明-自定义背景色过渡
self.zx_navBarBackgroundColorAlpha = 0;
#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,通知控制器设置导航栏不透明时的效果
//changingBlock:导航栏透明度正在改变时的回调
//transparentBlock:导航栏切换到透明状态时的回调(默认透明度0.7为临界点)
//opaqueBlock:导航栏切换到不透明状态时的回调(默认透明度0.7为临界点)
[self zx_setNavTransparentGradientsWithScrollView:scrollView fullChangeHeight:100 changeLimitNavAlphe:0.7 transparentGradientsChangingBlock:^(CGFloat alpha) {
//导航栏透明度正在改变时候处理
} transparentGradientsTransparentBlock:^{
//导航栏透明时的额外效果设置
} transparentGradientsOpaqueBlock:^{
//导航栏不透明时的额外效果设置
}];
}