UINavigationBar使用详解

基本使用

// 设置导航栏的标题
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;
}

问题:

通常我们直接在viewWillAppearviewWillDisappear中修改导航栏的颜色或者图片、隐藏导航栏等属性时,容易出现问题:

  1. 前后两个页面如果颜色或者图片不一样,这样设置动画切换时很突兀 。
  2. 被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]];
}

效果图如下:


UINavigationBar使用详解_第1张图片

设置返回按钮

我这里只讲解怎么使用系统的返回按钮,至于自定义的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];
}

效果图如下:


UINavigationBar使用详解_第2张图片

类似微信的返回+关闭按钮

@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;
}

效果图如下:


UINavigationBar使用详解_第3张图片

拦截返回事件

我们可以为 UINavigatonController 创建一个 Category,来定制navigationBar: shouldPopItem:的逻辑。这里需要注意的是,我们不需要去设置 delegate,因为 UINavigatonController 自带的 UINavigationBar 的 delegate 就是导航栏本身。那在实际的 Controller 里面怎么控制呢?因此同样需要对 UIViewController 添加一个 Protocol,这样在 Controller 中使用该 Protocol 提供的方法即可进行控制了。

一、UIViewController 添加 category
@protocol WDBackButtonHandlerProtocol 
@optional
// 重写下面的方法以拦截导航栏返回按钮点击事件,返回 YES 则 pop,NO 则不 pop
- (BOOL)wd_navigationShouldPopOnBackButton;
@end

@interface UIViewController (WDCustom) 

@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。

代码在这里

参考

  • UINavigationBar 使用总结
  • iOS拦截导航栏返回按钮事件的正确方式

你可能感兴趣的:(UINavigationBar使用详解)