基本使用
// 设置导航栏的标题
self.navigationItem.title = @"UINavigationBar使用总结";
// 设置导航栏的背景颜色
self.navigationController.navigationBar.barTintColor = [UIColor redColor];
// 设置导航栏的背景图片
[self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"Background"]
forBarMetrics:UIBarMetricsDefault];
更改状态栏的颜色
我们设置背景色或者背景图之后,状态栏依然还是默认的黑色,这样感觉不好看
UIStatusBarStyleDefault
,系统的默认样式,黑色内容,用于浅色的背景(如白色)
-
UIStatusBarStyleLightContent
白色内容,用于深色的背景(如红色)
在你的ViewController中添加下面的方法:
- (UIStatusBarStyle)preferredStatusBarStyle{
return UIStatusBarStyleLightContent;
}
问题:
通常我们直接在
viewWillAppear
和viewWillDisappear
中修改导航栏的颜色或者图片、隐藏导航栏等属性时,容易出现问题:
- 前后两个页面如果颜色或者图片不一样,这样设置动画切换时很突兀 。
- 被push进来的入口有很多,需要知道上层
viewController
的导航栏属性,不然在viewWillDisappear
中容易设错属性。
自定义导航栏(完美契合系统)
因此我们往往需要自定义一个导航栏,来满足我们的需求,比如?改造系统导航栏。
一、构建基类BaseViewController
头文件
@interface BaseViewController : UIViewController
@property (nonatomic, copy) UIColor *navBarColor;
@property (nonatomic, copy) UIImage *navBarImage;
@property (nonatomic, assign) CGFloat navBarAlpha;
@end
实现文件
@interface BaseViewController ()
@property (nonatomic, strong) UIImageView *backCustomView;
@end
@implementation BaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 将背景图设置为空图片,这样导航栏就是透明的了
[self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault];
}
// 插入自定义的图片,所有更改颜色、背景图、透明度等属性都是修改backCustomView属性
- (UIImageView *)backCustomView {
if (!_backCustomView) {
_backCustomView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth(self.navigationController.navigationBar.frame), 64)];
[self.view addSubview:_backCustomView];
}
return _backCustomView;
}
- (void)setNavBarColor:(UIColor *)navBarColor {
_navBarColor = [navBarColor copy];
self.backCustomView.backgroundColor = navBarColor;
}
- (void)setNavBarImage:(UIImage *)navBarImage {
self.backCustomView.image = navBarImage;
}
- (void)setNavBarAlpha:(CGFloat)navBarAlpha {
self.backCustomView.alpha = navBarAlpha;
}
二、继承基类,调用基类属性
#import "BaseViewController.h"
@interface FirstViewController : BaseViewController
@end
@implementation FirstViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.navigationItem.title = @"First";
self.navBarColor = [UIColor redColor];
}
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationItem.title = @"Second";
self.navBarImage = [UIImage imageForColor:[UIColor cyanColor]];
}
效果图如下:
设置返回按钮
我这里只讲解怎么使用系统的返回按钮,至于自定义的UIBarButtonItem* leftBarButton = [[UIBarButtonItem alloc] initWithCustomView:leftButtonView];
不是我讲解的重点。
如果当前viewController
是push进来的,且没设置self.navigationItem.leftBarButtonItem
,那么导航栏默认是有系统返回按钮的,且title是上层viewController
的title,可以统一修改返回标题为“返回”。
@implementation BaseViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
// 需要在基类中设置
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:nil];
}
效果图如下:
类似微信的返回+关闭按钮
@implementation SecondViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.navigationItem.title = @"Second";
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(backClicked)];
self.navigationItem.leftItemsSupplementBackButton = YES;
// 解决左滑手势失效问题
self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
}
效果图如下:
拦截返回事件
我们可以为 UINavigatonController
创建一个 Category,来定制navigationBar: shouldPopItem:
的逻辑。这里需要注意的是,我们不需要去设置 delegate,因为 UINavigatonController
自带的 UINavigationBar
的 delegate 就是导航栏本身。那在实际的 Controller 里面怎么控制呢?因此同样需要对 UIViewController 添加一个 Protocol,这样在 Controller 中使用该 Protocol 提供的方法即可进行控制了。
一、UIViewController 添加 category
@protocol WDBackButtonHandlerProtocol <NSObject>
@optional
// 重写下面的方法以拦截导航栏返回按钮点击事件,返回 YES 则 pop,NO 则不 pop
- (BOOL)wd_navigationShouldPopOnBackButton;
@end
@interface UIViewController (WDCustom) <WDBackButtonHandlerProtocol>
@end
二、UINavigationController 添加 category
#import "UINavigationController+WDCustom.h"
#import "UIViewController+WDCustom.h"
@implementation UINavigationController (WDCustom)
- (BOOL)navigationBar:(UINavigationBar *)navigationBar shouldPopItem:(UINavigationItem *)item {
BOOL shouldPop = NO;
UIViewController *topVC = [self topViewController];
BOOL canRespond = [topVC respondsToSelector:@selector(wd_navigationShouldPopOnBackButton)];
if (!canRespond) {
// 执行系统的pop
return YES;
}
// 判断自定义pop
shouldPop = [topVC wd_navigationShouldPopOnBackButton];
if (shouldPop) {
dispatch_async(dispatch_get_main_queue(), ^{
[self popViewControllerAnimated:YES];
});
} else {
// 取消 pop 后,复原返回按钮的状态
for (UIView *subview in navigationBar.subviews) {
if (subview.alpha > 0.0 && subview.alpha < 1.0) {
[UIView animateWithDuration:0.25 animations:^{
subview.alpha = 1.0;
}];
}
}
}
return NO;
}
使用方法很简单
1、在基类中:
#import "UINavigationController+WDCustom.h"
2、子类重写 protocol 中的 wd_navigationShouldPopOnBackButton
方法
@implementation SecondViewController
- (BOOL)wd_navigationShouldPopOnBackButton {
// do something, e.g. webview goback
return NO;
}
如果实现了该protocol,则可以做一些想做的事情,比如微信网页回退,推到最后一个就pop,return YES
就会触发pop事件。
也可以不实现该protocol,那么点击返回按钮,默认触发pop。
代码在这里