UINavigationBar的结构

ios中导航栏是开发中常用到的,之前从没有关注过导航栏里面的具体结构。今天抽空具体了解了一下

UINavigationBar的父view是一个叫做UILayoutContainerView的视图,该视图对应的控制器就UINavigationController。

UINavigationController主要三个部分:

  1. navigationBar
  2. viewcontrollers
  3. toolbar

导航栏半透明效果

初学ios时,在学习布局时,UIScrollView及其子类的布局和其它UIview的的布局稍有不同,由于在ios 7中导航栏加入了半透明效果,所以UIView的frame的布局Y点坐标是从导航栏顶部开始算起。而UIScrollView是从导航栏底部开始计算,因为UIViewController中有个属性automaticallyAdjustsScrollViewInsets(默认YES)可以当UIScrollView添加到控制器的view中时,其Y点坐标从导航栏底部开始计算。但是该属性在ios11被废弃,改用UIScrollView的contentInsetAdjustmentBehavior属性进行设置。

而造成这一原因就是导航栏的半透明效果。

一般设置self.navigationController.navigationBar.translucent = NO;就可以取消半透明效果。所有view的Y点都会从导航栏底部计算。

或者在AppDelegate文件方法didFinishLaunchingWithOptions设置

 [[UINavigationBar appearance] setTranslucent:NO];

?实用方法

// 透明全局(默认)
- (void)translucentAndAll{
    
    self.navigationController.navigationBar.translucent = YES;
    self.edgesForExtendedLayout = UIRectEdgeAll;
//    self.automaticallyAdjustsScrollViewInsets = YES;
//    self.extendedLayoutIncludesOpaqueBars = NO;
}

//有半透明效果,但是从导航栏底部计算
- (void)translucentAnd64{
    
    self.navigationController.navigationBar.translucent = YES;
    self.edgesForExtendedLayout = UIRectEdgeNone;
    //    self.automaticallyAdjustsScrollViewInsets = YES;
    //    self.extendedLayoutIncludesOpaqueBars = NO;
}

//没有半透明效果,从导航栏底部计算
- (void)noTranslucentAnd64{
    
    self.navigationController.navigationBar.translucent = NO;
   // self.edgesForExtendedLayout = UIRectEdgeNone;
}

//没有半透明效果,从导航栏顶部计算
- (void)noTranslucentAndAll{
    
    self.navigationController.navigationBar.translucent = NO;
    self.extendedLayoutIncludesOpaqueBars = YES;
   // self.edgesForExtendedLayout = UIRectEdgeNone;
}

导航栏具体结构

给导航栏添加一个背景色和标题,其它还原为默认设置

//导航栏背景色
self.navigationController.navigationBar.barTintColor = [ UIColor orangeColor];

启动项目,使用Debug View Hierarchy 进行检查。

左侧是当前view的一个结构图,UILayoutContainerView下面有两个平级的subview:

  1. UINavigationTransitionView:在控制器进行push和pop的操作的时候会用到该View,在转场动画里会用到。
  2. UINavigationBar:导航栏view

UINavigationBar

结构图如下

如图右侧navigationBar的Y点坐标是相对UILayoutContainerView为20(刘海屏是40)。导航栏的高度是44。

navigationBar下还有两个view,_UIBarBackground用于修改背景色等,另一个是ContentView:用于存放BarButton和title。

_UIBarBackground

是所有背景图的父view,默认情况下 _UIBarBackground下有两个View:

  •   UIImageView,当前的imageView是导航栏下面的一条黑线。如下图

       如图右侧,导航栏底部黑线的y点坐标是64,高度0.33,说明导航栏下面的黑线是超出navigationBar的范围的。

      去除导航栏底部黑线的常用下面方法,给这个ImageView一个空的图片对象。

[self.navigationController.navigationBar setShadowImage:[UIImage new]];

     也可以用

self.navigationController.navigationBar.clipsToBounds = YES;

      使得超出navigationBar范围的视图不展示。

  •  UIVIsualEffectView(ios8新增)

    这个view的作用就是实现导航栏的半透明效果,如果要实现毛玻璃效果可以使用该view。背景色设置在该view的子视图中。

如果把translucent设置为NO,导航栏的结构是否会发生什么变化?

我们发现UIVisualEffectView这个view没有了,可以推测实现半透明效果就是通过该view实现。并且背景色设置在了_UIBarBackground中。

_UINavigationBarContentVIew

_UIBarBackground在导航栏控制器是不会变的,当你在一个控制器修改了其背景色,退出该控制器是要还原其原来的背景色。

而_UINavigationBarContentView里面的值是不断变换的。通过下面的这个对象给其赋值

UIViewController有一个属性

@property(nonatomic,readonly,strong) UINavigationItem *navigationItem;

可以给当前控制器导航栏修改,中间及左右的按钮。UINavigationItem是一个继承于NSObeject的类,类似于一个model用于存储每个导航栏控制器中的数据,初始化时会有一些默认值。当我们根据需要可以给这个model进行添加或者修改一些参数。

每当跳转一个新的控制器,_UINavigationBarContentView里面的值就会发生改变。那么如何获取旧的控制器中导航栏的信息呢?

navigationBar中有一个属性items,是一个UINavigationItem类型的栈。栈底是第一个控制器的信息。

@property(nullable,nonatomic,copy) NSArray<UINavigationItem *> *items;

在开发中,如果没有特殊需要自定义的导航栏,添加背景图片时可以使用下面方法

- (void)setBackgroundImage:(nullable UIImage *)backgroundImage forBarMetrics:(UIBarMetrics)barMetrics

该方法是给导航栏添加一个背景图,在导航栏的view结构中会在_UIBarBackground中会在在黑色底线前面插入一个imageView。

发布了47 篇原创文章 · 获赞 7 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/a1034386099/article/details/100151055