iOS 定制导航栏

在 App 开发的过程中,经常会要求对顶部导航栏进行自定义。本文将介绍在基于 mPaaS 框架创建的页面中,自定义导航栏的方法。包括定制应用主题和定制某一个页面导航栏样式。

基础概念

导航栏元素分布

导航栏元素主要分布在三个区域。一般对导航栏的定制化需求,最终都会变为对这几个区域的修改:

edit.png
  1. back:返回按钮控制区域,由 mPaaS 页面基类创建,默认样式为返回箭头 + 返回文案。

  2. title/subTitle:标题栏控制区域,默认不显示。若需显示,请调用系统方法设置当前页面的 title。

  3. optionMenu:页面菜单选项区域,默认不显示。如需显示,请调用系统方法设置当前页面的 rightNavigationItem。

导航栏结构

  • 如下图所示,基于 mPaaS 框架创建的应用,默认的 UI 结构为:window/navigationController > tabViewController > 每个 tab 嵌入一个 viewController。即应用主 window 的根应用是一个 UINavigationController 的对象,UINavigationController 的根应用是一个 UITabViewController导航栏结构

  • 由以上 UI 结构可以看出,整个应用全局只有一个 navigationController,因此所有页面共用同一个导航栏(默认使用 APNavigationBar 创建)。

    导航栏结构-UI.png
  • 为了统一所有页面的导航栏样式,要求 mPaaS 应用中,所有页面所在的 VC 都要继承 DTViewControler,包括 native 和 H5 页面。

  • 基于 mPaaS 框架创建的应用的默认主题,主白底黑字蓝按钮:

    ddd

定制应用主题

每个应用都会有自己的主题风格,根据以下描述修改 mPaaS 应用的默认主题:

  • 修改导航栏背景色、返回控制区域、标题控制区域等,可重写 AUThemeManager 类的 au_defaultTheme_extraInfo 方法,修改以下 key 对应的返回值。

    • 接口方法

      @interface AUThemeManager(AUExtendinfo)
      /*支付宝客户端存在默认主题,独立 App 可修改该默认值
      * 在该方法中只需返回与默认主题不同的键值对即可,请使用 AUTheme.h 中定义好的 key
      */
      +(NSDictionary *)au_defaultTheme_extraInfo;
      
      @end
      /*
      *  例如
      *  +(NSDictionary*)au_defaultTheme_add_Info
      *  {
      *    NSMutableDictionary *dict = [INSMutableDictionary alloc] init];
      *    dictITITLEBAR_BACKGROUND_COLOR] = AU_COLOR_APP_GREEN; // AUTitleBar 背景色
      *    dit[TITLEBAR TITLE TEXTCOLOR1 = [UIColor redColor];   // AUTitleBar 标题色
      *    ...
      *    return dict;
      *  }
      */
    • 代码示例

      @implementation AUThemeManager (Portal)
      
      + (NSDictionary *)au_defaultTheme_extraInfo
      {
          NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
          dict[TITLEBAR_BACKGROUND_COLOR] = @"COLOR(#108EE9,1)"; // 导航栏背景色
          dict[TITLEBAR_LINE_COLOR] = @"COLOR(#108EE9,1)";       // 导航栏底部分割线或边线的颜色
          dict[TITLEBAR_TITLE_TEXTCOLOR] = @"COLOR(#ffffff,1)";  // 导航栏标题色
          dict[TITLEBAR_TITLE_TEXTSIZE_BOLD] = @"FONT(18)";      // 导航栏标题大小
          dict[TITLEBAR_TEXTCOLOR] = @"COLOR(#ffffff,1)";        // 导航栏返回按钮颜色
      
          return dict;
      }
      
      @end
      }
      说明

      颜色值必须使用类如 COLOR(#108EE9,1) 的方式,否则会报错。

  • 修改主题配置中返回按钮图片,需重写 AUBarButtonItem 类中的 au_default_backButtonImg 方法。

    • 接口方法

      #import "AUUILoadDefine.h"//程序自动生成
      #ifdef ANTUI_UI_TitleBar_AUBarButtonltem//程序自动生成
      //
      // AUBarButtonltem+AUExtendInfo.h
      // AntUI
      //
      // Copyright © 2017 Alipay. All rights reserved.
      //
      #import "AUBarButtonltem.h"
      
      @interface AUBarButtonltem(AUExtendInfo)
      
      //支付宝返回按钮默认是蓝色 icon,独立 App 可修改返回按钮默认图标
      +(UIImage *)au_default_backButtonlmg;
      
      @end
    • 代码示例

      @implementation AUBarButtonItem (CGBBarButtonItem)
      
      + (UIImage *)au_default_backButtonImg
      {
          // 自定义返回按钮的图片
          return  APCommonUILoadImage(@"back_button_normal_white");
      
      }
      @end
  • 修改所有页面的返回按钮样式和文案。

定制某一个页面导航栏样式

除了定制主题外,有时也需定制当前页面的导航栏的样式,如修改背景颜色、返回按钮样式等,根据修改时机不同,mPaaS 提供了不同的方法。

  • 页面加载前,在默认导航栏样式基础上修改导航栏颜色,可以在当前页面所在的 VC 中,实现 DTNavigationBarAppearanceProtocol 中的定义方法,来修改对应区域的颜色。

    • 接口方法

      @protocol DTNavigationBarAppearanceProtocol<NSObject>
      
      @optional
      
      /** 这个 DTViewController 是否要自动隐藏navigationBar,默认为 NO。业务某个 ViewController 需要隐藏 NavigationBar 可以重载此方法并返回 YES.
      **/
      -(BOOL)autohideNavigationBar;
      
      /** 当前 VC 隐藏导航栏后,如果需要设置一个全透明的导航栏,且当前页面需设置与框架逻辑一致的返回文案,请重载此方法,并返回一个 APCustomNavigationView 的实例
      -(UIView *)customNavigationBar;
      
      /** 如果某个 viewcontroller 希望自己的 titlebar 是不透明,并且指定一个颜色,可以重写这个方法,并返回希望的颜色。
      * 仅限于被 Push 的 VC,tabbar 里的 VC 还是不允许修改 navigationBar 的半透明属性
      */
      -(UIColor *)opaqueNavigationBarColor;
      
      /**
       *  如果某个viewcontroller希望修改状态栏的样式,请重写此方法,并返回希望的style
       */
      - (UIStatusBarStyle)customStatusBarStytle;
      
      /**
       *  如果某个viewcontroller希望修改导航栏标题的颜色,请重写此方法,并返回希望的颜色
       */
      - (UIColor *)customNavigationBarTitleColor;
    • 代码示例

      #pragma mark DTNavigationBarAppearanceProtocol:进入页面时修改导航栏样式
      - (UIColor *)opaqueNavigationBarColor
      {
          // 设置当前页面导航栏背景为红色
          return [UIColor redColor];
      
      //    // 设置当前页面导航栏透明
      //    return [UIColor colorWithRGB:0xff0000 alpha:0];
      }
      
      - (BOOL)autohideNavigationBar
      {
          // 设置当前页面导航栏是否隐藏
          return NO;
      }
      
      - (UIStatusBarStyle)customStatusBarStytle
      {
          // 设置当前页面状态栏样式
          return UIStatusBarStyleDefault;
      }
      
      - (UIColor *)customNavigationBarBackButtonTitleColor
      {
          // 设置当前页面返回按钮文案颜色
          return [UIColor greenColor];
      }
      
      - (UIImage *)customNavigationBarBackButtonImage
      {
          // 设置当前页面返回按钮图片
          return APCommonUILoadImage(@"back_button_normal_white");
      }
      
      - (UIColor *)customNavigationBarTitleColor
      {
          // 设置当前页面标题颜色
          return [UIColor greenColor];
      }
  • 页面打开后,在用户操作的过程中动态修改导航栏样式,如背景颜色滑动渐变、修改右侧菜单按钮等,根据修改的区域不同,主要分为以下几类:

    • 背景区域:包括隐藏/显示导航栏、透明导航栏、修改导航栏背景颜色、修改状态栏颜色。

      - (void)gotoHideNavigator
      {
          // 隐藏导航栏
          [self.navigationController.navigationBar setHidden:YES];
      }
      
      - (void)gotoShowNavigator
      {
          // 显示导航栏
          [self.navigationController.navigationBar setHidden:NO];
      }
      
      - (void)gotoTransparency
      {
          // 透明导航栏
          [self.navigationController.navigationBar setNavigationBarTranslucentStyle];
      }
      
      - (void)gotoUpdateBackgroundColor
      {
          // 修改导航栏背景颜色
          [self.navigationController.navigationBar setNavigationBarStyleWithColor:[UIColor whiteColor] translucent:NO];
          [self.navigationController.navigationBar setNavigationBarBottomLineColor:[UIColor whiteColor]];
      }
      
      - (void)gotoUpdateStatusBarStyle
      {
          // 修改状态栏颜色
          [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent];
      }
    • 返回控制区域:修改默认返回按钮文案颜色、修改默认返回按钮返回箭头样式、重新设置返回按钮样式。

      - (void)gotoUpdateBackTitleColor
      {
          // 修改默认返回按钮文案颜色
          NSArray *leftBarButtonItems = self.navigationItem.leftBarButtonItems;
          if ([leftBarButtonItems count] == 1) {
              if (leftBarButtonItems[0] && [leftBarButtonItems[0] isKindOfClass:[AUBarButtonItem class]]) {
                  AUBarButtonItem *backItem = leftBarButtonItems[0];
                  backItem.titleColor = [UIColor blackColor];
              }
          }
      }
      
      - (void)gotoUpdateBackImage
      {
          // 修改默认返回按钮返回箭头样式
          NSArray *leftBarButtonItems = self.navigationItem.leftBarButtonItems;
          if ([leftBarButtonItems count] == 1) {
              if (leftBarButtonItems[0] && [leftBarButtonItems[0] isKindOfClass:[AUBarButtonItem class]]) {
                  AUBarButtonItem *backItem = leftBarButtonItems[0];
                  backItem.backButtonImage = APCommonUILoadImage(@"back_button_normal");
              }
          }
      }
      
      - (void)gotoUpdateBackItem
      {
          // 重新设置返回按钮样式
          self.navigationItem.leftBarButtonItem = [AUBarButtonItem barButtonItemWithImageType:AUBarButtonImageTypeDelete target:self action:@selector(onClickBack)];
      }
      
      - (void)onClickBack
      {
          [self.navigationController popViewControllerAnimated:YES];
      }
    • 标题控制区域:修改默认标题颜色、设置上下主副标题、修改标题为图片显示。

      - (void)gotoUpdateTitleColor
      {
          // 修改标题颜色
          [self.navigationController.navigationBar setNavigationBarTitleTextAttributesWithTextColor:[UIColor blackColor]];
      }
      
      - (void)gotoTwoTitle
      {
          // 修改标题样式:上下主副标题
          self.navigationItem.titleView = [[AUDoubleTitleView alloc] initWithTitle:@"主标题" detailTitle:@"副标题"];
      }
      
      - (void)gotoTitleImage
      {
          // 修改标题样式:图片
          UIImageView *imageView = [[UIImageView alloc] initWithImage:APCommonUILoadImage(@"ilustration_ap_expection_alert")];
          imageView.frame = CGRectMake(0, 0, self.self.view.width-100, 64);
          self.navigationItem.titleView = imageView;
      }
    • 菜单控制区域:设置单个或多个右侧菜单按钮。

      - (void)gotoSetOptionMenu
      {
          // 设置右侧单按钮
          self.navigationItem.rightBarButtonItem = [AUBarButtonItem barButtonItemWithImageType:AUBarButtonImageTypeGroupChat target:self action:@selector(onClickRightItem)];
      }
      
      - (void)gotoSetTwoOptionMenu
      {
          // 设置右侧双按钮
          AUBarButtonItem *item1 = [AUBarButtonItem barButtonItemWithImageType:AUBarButtonImageTypeGroupChat target:self action:@selector(onClickRightItem)];
          AUBarButtonItem *item2 = [AUBarButtonItem barButtonItemWithImageType:AUBarButtonImageTypeHelp target:self action:@selector(onClickRightItem)];
          self.navigationItem.rightBarButtonItems = @[item1, item2];
      }
  • 沉浸式导航栏:进入时导航栏透明,滑动到指定位置后不透明。主要分为以下两类:

    • 进入页面时,设置导航栏透明:在当前页面所在的 VC 中重写以下接口。

      - (UIColor *)opaqueNavigationBarColor
        {
            // 设置当前页面导航栏透明
            return [UIColor colorWithRGB:0xff0000 alpha:0];
        }
    • 页面滑动到指定位置后,修改导航栏背景区域、返回区域、标题区域及菜单控制区域等样式。

        - (void)gotoUpdateBackgroundColor
        {
            // 修改导航栏背景颜色
            [self.navigationController.navigationBar setNavigationBarStyleWithColor:[UIColor whiteColor] translucent:NO];
            [self.navigationController.navigationBar setNavigationBarBottomLineColor:[UIColor whiteColor]];
        }
      
        - (void)gotoUpdateBackTitleColor
        {
            // 修改默认返回按钮文案颜色
            NSArray *leftBarButtonItems = self.navigationItem.leftBarButtonItems;
            if ([leftBarButtonItems count] == 1) {
                if (leftBarButtonItems[0] && [leftBarButtonItems[0] isKindOfClass:[AUBarButtonItem class]]) {
                    AUBarButtonItem *backItem = leftBarButtonItems[0];
                    backItem.titleColor = [UIColor blackColor];
                }
            }
        }
      
        - (void)gotoUpdateTitleColor
        {
            // 修改标题颜色
            [self.navigationController.navigationBar setNavigationBarTitleTextAttributesWithTextColor:[UIColor blackColor]];
        }