UINavigationBar-使用总结

简介

UINavigationBar是用于实现管理层级关系内容的组件,直接继承自UIView。通常用在UINavgationController类中,用于管理和显示UINavgationControllersubViewController , 同时UINavgationBar也可以单独使用,添加至任何的UIView中。UINavigationBar比较重要的属性为,左侧按钮,中间的标题,以及右侧按钮。

 NS_CLASS_AVAILABLE_IOS(2_0) @interface UINavigationBar : UIView <NSCoding, UIBarPositioning>
在导航控制器中的简单使用
  1. 获取导航条

    UINavigationBar *navBar = self.navigationController.navigationBar;
    //设置导航栏 title
    self.navigationItem.title = @"UINavigationBar使用总结";
    
  2. 设置导航栏背景色

    //通过barTintColor来设置背景色
    self.navigationController.navigationBar.barTintColor = [UIColor redColor];//iOS7 以上系统  
    

    navigation back bar tint Color

  3. 设置导航栏的背景图片

    [self.navigationController.navigationBar setBackgroundImage:[UIImage imageNamed:@"Background"] forBarMetrics:UIBarMetricsDefault];
    

    navigation Bar background

    在这里得稍微说说UIBarMetrics这个枚举, 它主要是用来控制在不同状态下导航栏的显示。和UIButton

    - (void)setBackgroundImage:(nullable UIImage *)image forState:(UIControlState)state
    

    这个方法有点类似。

    typedef NS_ENUM(NSInteger, UIBarMetrics) {
        UIBarMetricsDefault,
        UIBarMetricsCompact,
        UIBarMetricsDefaultPrompt = 101, // Applicable only in bars with the prompt property, such as UINavigationBar and UISearchBar
        UIBarMetricsCompactPrompt,
        UIBarMetricsLandscapePhone NS_ENUM_DEPRECATED_IOS(5_0, 8_0, "Use UIBarMetricsCompact instead") = UIBarMetricsCompact,
        UIBarMetricsLandscapePhonePrompt NS_ENUM_DEPRECATED_IOS(7_0, 8_0, "Use UIBarMetricsCompactPrompt") = UIBarMetricsCompactPrompt,
    };
    
  4. 设置导航栏样式

    [navBar setBarStyle:UIBarStyleDefault];  
       分别有如下几种样式:  
    typedef NS_ENUM(NSInteger, UIBarStyle) {  
        UIBarStyleDefault          = 0,  
        UIBarStyleBlack            = 1,  
        UIBarStyleBlackOpaque      = 1, // Deprecated. Use UIBarStyleBlack  
        UIBarStyleBlackTranslucent = 2, // Deprecated. Use UIBarStyleBlack and set the translucent property to YES
    };    
    

    注意:我们发现,在后面两个标记为Deprecated,我们知道使用后面两种将不被提倡。 从枚举中,我们也可以看出:UIBarStyleBlack=1和UIBarStyleBlackOpaque=1表示为一样的。 后来,发现增加了一个方法:[navBar setTranslucent:YES];用来指示是否透明。 所以,我们使用UIBarStyleDefault和UIBarStyleBlack来定义UINavigationBar样式,并且用setTranslucent:方法来设置透明与否。

    //使用方式
    switch (buttonIndex) {
       case 0: // "Default"
           self.navigationController.navigationBar.barStyle = UIBarStyleDefault;
           // Bars are translucent by default.
           self.navigationController.navigationBar.translucent = YES;
           // Reset the bar's tint color to the system default.
           self.navigationController.navigationBar.tintColor = nil;
           self.navigationController.navigationBar.barTintColor = nil;//iOS7 以上系统
    
    
           break;
       case 1: // "Black Opaque"
           self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
           self.navigationController.navigationBar.translucent = NO;
           self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
           break;
       case 2: // "Black Translucent"
           self.navigationController.navigationBar.barStyle = UIBarStyleBlack;
           self.navigationController.navigationBar.translucent = YES;
           self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
           self.navigationController.navigationBar.barTintColor = [UIColor redColor];
           break;
    }
    
  5. 更改顶部状态栏的颜色 修改完导航栏的颜色之后,顶部状态栏可能因为导航栏的颜色相近而现实不清楚,所以有时候需要修改状态栏的颜色 状态栏的颜色修改:系统给我们提供了UIStatusBarStyleDefaultUIStatusBarStyleLightContent两种样式供我们选择。

       UIStatusBarStyleDefault,系统的默认样式,黑色内容,用于浅色的背景(如白色)
       UIStatusBarStyleLightContent 白色内容,用于深色的背景(如红色)
    
    • 下面来看看具体怎么实现,主流的实现方式是分两步:
      a. 在工程的Info.plist文件中添加一行UIViewControllerBasedStatusBarAppearance,选择Boolean类型,并设置为YES,Xcode会自动把名称变为View controller-based status bar appearance
      b. 在你的ViewController中添加下面的方法

      -(UIStatusBarStyle)preferredStatusBarStyle{
        return UIStatusBarStyleLightContent;
      }   
      

      另外,特别需要注意的是,如果你的ViewController是通过navigationController push进来的,还需要加下面一句代码才能生效: self.navigationController.navigationBar.barStyle = UIBarStyleBlack; 具体,可参考UIStatusBarStyle PreferredStatusBarStyle does not work on iOS 7

    恩,我们来看看运行效果。
    naviagation

  6. 设置返回按钮 从上面的效果图中我们可以看到返回按钮还是默认的蓝色按钮,下面我将会大家来介绍返回按钮的个性化。

    • 设置返回按钮的颜色 只需要设置tintColor属性即可

      self.navigationController.navigationBar.tintColor = [UIColor whiteColor];
      

      得到的效果图如下:
      naviagation

    • 只设置返回按钮的图片

      - (void)goToBack {
          [self.navigationController popViewControllerAnimated:YES];
      }
      
      - (void)setBackButtonWithImage {
          UIImage *leftButtonIcon = [[UIImage imageNamed:@"LeftButton_back_Icon"]
                                     imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
          UIBarButtonItem *leftButton = [[UIBarButtonItem alloc] initWithImage:leftButtonIcon
                                                                         style:UIBarButtonItemStyleBordered
                                                                        target:self
                                                                        action:@selector(goToBack)];
          self.navigationItem.leftBarButtonItem = leftButton;
      
          //修复navigationController侧滑关闭失效的问题
          self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;
      }
      

      得到的效果如下:
      naviagation

      这里需要注意的地方有三点:

      1. 需要自己实现返回按钮的事件。
      2. 特别的解释下UIImageimageWithRenderingMode:方法,参数UIImageRenderingModeAlwaysOriginal 表示总是用原图渲染,如果不这么设置,返回按钮将会显示tintColor的颜色(默认为蓝色)。UITabbarItem也存在同样地问题。
      3. 我们自己设置返回按钮,会导致系统的侧滑关闭效果失效。添加上面代码中最后一句代码即可修复(self.navigationController.interactivePopGestureRecognizer.delegate = (id)self;)。
    • 仅设置返回按钮的文字

      - (void)setBackButtonTitle {
          UIBarButtonItem *leftButton = [[UIBarButtonItem alloc]
                  initWithTitle:NSLocalizedString(@"取消", nil)  
                  style:UIBarButtonItemStylePlain  
                  target:self action:@selector(goToBack)];
          leftButton.tintColor = [UIColor whiteColor];
          self.navigationItem.leftBarButtonItem = leftButton;
      }
      

      得到的效果如下:

    • 自定义返回按钮
      如果上面几种方式还无法满足你的要求(比如,需要同时设置返回按钮文字和图片),就需要用到UIBarButtonItem的initWithCustomView方法。

      - (void)setCustomLeftButton {
          UIView* leftButtonView = [[UIView alloc]initWithFrame:CGRectMake(0, 0, 60, 40)];
          UIButton* leftButton = [UIButton buttonWithType:UIButtonTypeSystem];
          leftButton.backgroundColor = [UIColor clearColor];
          leftButton.frame = leftButtonView.frame;
          [leftButton setImage:[UIImage imageNamed:@"LeftButton_back_Icon"] forState:UIControlStateNormal];
          [leftButton setTitle:@"返回" forState:UIControlStateNormal];
          leftButton.tintColor = [UIColor redColor];
          leftButton.autoresizesSubviews = YES;
          leftButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
          leftButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;
          [leftButton addTarget:self action:@selector(goToBack) forControlEvents:UIControlEventTouchUpInside];
          [leftButtonView addSubview:leftButton];
          UIBarButtonItem* leftBarButton = [[UIBarButtonItem alloc] initWithCustomView:leftButtonView];
          self.navigationItem.leftBarButtonItem = leftBarButton;
      }  
      

      得到的效果图如下:

      设置rightBarButtonItem基本上脱离不了上面的几种方式,大家可以参照上面返回按钮的设置方式。

  7. 隐藏导航栏底部的线条
    有时候遇到一些特殊的要求,需要隐藏导航栏底部的线条。 两行代码就可以做到。

    • 设置导航栏的背景图(setBackgroundImage方法)
    • 设置导航栏的shadowImage (setShadowImage方法)

      UINavigationBar *navigationBar = self.navigationController.navigationBar;
      //设置透明的背景图,便于识别底部线条有没有被隐藏
      [navigationBar setBackgroundImage:[[UIImage alloc] init]
                             forBarPosition:UIBarPositionAny
                                 barMetrics:UIBarMetricsDefault];
      //此处使底部线条失效
      [navigationBar setShadowImage:[UIImage new]];
      

      来看看效果图:

    • 还有一种做法,一行代码就可以达到效果

      //方法二:
      self.navigationController.navigationBar.clipsToBounds = YES;  
      

      想要知道更详细的内容可以参考这个页面:How to hide iOS7 UINavigationBar 1px bottom line

  8. 设置导航条底部线条的颜色
    有了上面的基础,设置导航栏线条的颜色就变得很简单了。
    首先,我做了个UIImage的分类:通过颜色转成UIImage;
    然后,用上面的方案来设置导航栏底部线条。

    颜色转图片的代码:

    @implementation UIImage (ColorImage)
    
    + (UIImage *)imageWithColor:(UIColor *)color
    {
        CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f);
        UIGraphicsBeginImageContext(rect.size);
        CGContextRef context = UIGraphicsGetCurrentContext();
    
        CGContextSetFillColorWithColor(context, [color CGColor]);
        CGContextFillRect(context, rect);
    
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    
        return image;
    }
    
    @end
    

    设置导航栏底部线条颜色的代码:

    UINavigationBar *navigationBar = self.navigationController.navigationBar;
    [navigationBar setBackgroundImage:[[UIImage alloc] init]
                            forBarPosition:UIBarPositionAny
                                barMetrics:UIBarMetricsDefault];
    //此处使底部线条颜色为红色
    [navigationBar setShadowImage:[UIImage imageWithColor:[UIColor redColor]]]; 
    

    依照惯例,看下效果图:

    当然还有其他的方式也可以做到,如addSubview,addSubLayer等。感兴趣的话可以参考下这个页面:iOS7 - Change UINavigationBar border color

  9. 在导航栏上添加多个按钮
    以微信打开网页时的效果为例,效果图如下,有两个按钮:返回和关闭。
    image

    有下面两种方式可供选择,但是最终还是要用到leftBarButtonItems这个方法。

    #define UserMethod1 0
    
    UIBarButtonItem *closeItem = [[UIBarButtonItem alloc] initWithTitle:@"关闭" style:UIBarButtonItemStylePlain target:self action:@selector(closeAction)];
    if (UserMethod1) {
        //方法一:
        self.navigationItem.leftBarButtonItems = @[closeItem];
        //要求显示默认的返回按钮,但是文字会显示默认的Back,暂时还不知道这个文字怎么改
        self.navigationItem.leftItemsSupplementBackButton = YES;
    }
    else {
        //方法二
        UIButton* leftButton = [UIButton buttonWithType:UIButtonTypeSystem];
        leftButton.backgroundColor = [UIColor clearColor];
        leftButton.frame = CGRectMake(0, 0, 45, 40);
        [leftButton setImage:[UIImage imageNamed:@"LeftButton_back_Icon"] forState:UIControlStateNormal];
        [leftButton setTitle:@"返回" forState:UIControlStateNormal];
        leftButton.tintColor = [UIColor whiteColor];
        leftButton.autoresizesSubviews = YES;
        leftButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
        leftButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleLeftMargin;
        [leftButton addTarget:self action:@selector(goToBack) forControlEvents:UIControlEventTouchUpInside];
        UIBarButtonItem* backItem = [[UIBarButtonItem alloc] initWithCustomView:leftButton];
    
        self.navigationItem.leftBarButtonItems = @[backItem,closeItem];
    }
    

    然后,运行的效果图如下:

    方法一用到了leftItemsSupplementBackButton 这个属性,会显示系统默认的返回按钮,但是文字也是显示默认的Back文字,目前还没找到怎么修改这个文字,如果有谁知道,还请不吝赐教;所以我暂时还是建议大家用方法二。相应的还有 rightBarButtonItems 这个属性,如果要在导航栏右侧展示多个按钮的话,可以设置这个属性。

  10. 在导航栏上添加分段控件

    这次,以QQ为例,代码如下:

    UISegmentedControl *segControl = [[UISegmentedControl alloc] initWithItems:@[@"消息",@"电话"]];
    segControl.tintColor = [UIColor colorWithRed:0.07 green:0.72 blue:0.96 alpha:1];
    [segControl setSelectedSegmentIndex:0];
    self.navigationItem.titleView = segControl;
    

    代码很简单,就是设置 titleView 这个属性,当然,你也可以把这个属性设置为你自定义的View.

  11. 导航栏全局属性设置

    //全局设置导航栏主题
    - (void)setNavigationControllerAppearance {
        [UINavigationBar appearance].barStyle  = UIBarStyleBlack;
        [[UINavigationBar appearance] setBarTintColor:[UIColor colorWithWhite:0.1 alpha:0.5]];
        [[UINavigationBar appearance] setTintColor:[UIColor whiteColor]];
    }
    

    全局设置导航栏的好处有两个:一是不用对每个 NavigationBar 进行设置;二是方便做主题管理,切换主题,只需要更改全局设置即可。

  12. 与导航栏相关的一些开源组件

    1. NJKWebViewProgress - 类似于Safiri加载网页时的进度显示

  13. FDFullscreenPopGesture 一个丝滑的全屏滑动返回手势
    原文介绍链接

UINavigationItemUINavigationBar 关系分

navigationcontroller 直接控制 viewcontrollers,然后包含的 navigationbar 形成整个nv的导航栏,bar并包含整个navigationItem 的栈,管理整个nv的 navigationitem( NSArray *items 属性)。

navigationItem 包含了bar视图的全部元素(如title,tileview,backBarButtonItem等),受当前viewcontroller管理,即bar形成整个nv的导航视图,然后每个nv页面的导航栏元素由所在页面的 navigationItem 管理。即设置当前页面的左右barbutton,用 self.navigationItem.leftBarButtonItem 等。

Code Demo