NavigationBar这个最基本的控件想必大家都接触的不少,更是有各种各样的自定义的方式。我这边也分享一种根据项目需求而构想的自定义NavigationBar,可能并不是很通用,权当参考。
先看下项目界面简图:
Bar左边三个控件,返回按钮,logo图片,当前页面的title相对来说是固定的,除了最基础的几个Tab页面不需要返回按钮,每个页面都有这三个控件。
Bar右边是不同的功能按钮,数量0-3个不等。
接下来具体实现。
采用UIView来模拟NavigationBar,所以在NavigationController中先隐藏navigaitonBar
self.navigationBar.hidden = YES;
然后将自定义的navigationBar(下称CustomNaviBar)添加到NavigationController中。
CustomNaviBar初始创建时只添加logo imageView和title Label这两个控件。
CustomNaviBar提供如下几个方法:
/**
更新当前页面title
页面初始化时调用 (建议viewDidAppear)
*/
- (void)updateNaviBarTitle:(NSSring *)title;
更新title方法实现即给Label赋值,设置布局,这里提一个小点,为了某些页面title是灵活配置的布局正常,传入的title去除收尾空格。
self.titleLabel.text = [title stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
/**
添加返回按钮
push到下一个页面时,在NavigationController中调用
*/
- (void)remakeSubViewsForBackBtn;
这个方法顾名思义,添加返回按钮。方法的实现也仅仅是添加按钮,调整控件布局。
在调用时,可以在NavigationController中调用
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated {
[super pushViewController:viewController animated:YES];
[self.baseNaviBar remakeSubViewsForBackBtn];
}
/**
清除返回按钮
*/
- (void)clearForBackBtn;
有创建也就有移除,返回到基础Tab页面时需要调用此方法来清除返回按钮。
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.naviController.baseNaviBar updateNaviBarTitle:@"主页"];
[self.naviController.baseNaviBar clearForBackBtn];
}
/**
单独添加一个右边的按钮
*/
- (void)addSingleRightBtnWithTitle:(NSString *)title ButtonImage:(NSString *)imageName Target:(id)target Action:(SEL)action;
刚刚提到右侧的功能键数量0-3个不等,这里先给到一个创建一个按钮的方法,方法参数中传入target以及点击事件。
此方法的实现中,除了给btn布局和添加点击事件以外,需要提一小点是
[self.rightButton setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisHorizontal];
[self.titleLabel setContentCompressionResistancePriority:UILayoutPriorityDefaultLow forAxis:UILayoutConstraintAxisHorizontal];
设置约束的优先级,以免某些页面title文字过长导致和右侧按钮约束冲突。
/**
添加自定义按钮
由右向左添加!
填入buttonTitles参数也按照从右向左的顺序。
buttonConfiguration:
@{
@"title":@"标题",
@"image":@"图片名",
@"action":action,//button点击方法,传入NSInvocation类型参数
}
*/
- (void)addButtonWithButtonConfiguration:(NSDictionary *)buttonConfiguration,...NS_REQUIRES_NIL_TERMINATION;
右侧有多个按钮的页面就由此方法来实现,比较关键的两点是:
1> NS_REQUIRES_NIL_TERMINATION 宏
在创建字典,数组时肯定没少用过这类方法
NSArray arrayWithObjects:<#(nonnull ObjectType), ...#>, nil
自己来实现这个写法时,核心代码如下:
- (void)addButtonWithButtonConfiguration:(NSDictionary *)buttonConfiguration, ... {
va_list argList;
if (buttonConfiguration) {
va_start(argList, buttonConfiguration);
NSDictionary *configuraDict;
//这样每次循环argList所代表的指针偏移量就不断下移直到取出nil
while ((configuraDict = va_arg(argList, id))) {
//这里设置btn布局,添加点击事件
}
}
va_end(argList);
}
2>传入NSInvocation类型参数
直接上代码,创建一个NSInvocation对象
+ (NSInvocation *)creatInvocationWithTarget:(id)target Action:(SEL)action {
NSMethodSignature *signature = [target methodSignatureForSelector:action];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.target = target;
invocation.selector = action;
return invocation;
}
NSInvocation有其他更有趣好用的使用场景,在此处大材小用了,仅仅是因为需要作为参数包装到字典中,其他用法这里先不赘述了。
添加btn方法调用示例如下:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.naviController.baseNaviBar updateNaviBarTitle:@"主页"];
[self.naviController.baseNaviBar clearForBackBtn];
[self.naviController.baseNaviBar addButtonWithButtonConfiguration:
@{@"title":@"btn1",@"image":@"image1",@"action":[CreatActionTool creatInvocationWithTarget:self Action:@selector(click1)]},
@{@"title":@"btn2",@"image":@"image2",@"action":[CreatActionTool creatInvocationWithTarget:self Action:@selector(click2)]},nil];
}
在添加btn点击事件时如下:
NSInvocation *action = [configuraDict objectForKey:@"action"];//configuraDict为在上述方法中循环取出的btn配置数据
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button addTarget:action.target action:action.selector forControlEvents:UIControlEventTouchUpInside];
提供主要功能的几个方法如上所述,还可以随意添加一些自定义功能。
因为代码中为了项目需求,做了好多适配,也不是通用的方法,后面整理好再上传~