修改NavigationBar一像素线颜色

来源:互联网 发布:有哪些数据网站 编辑:程序博客网 时间:2024/05/16 12:10

前言

有一个导航栏样式的需求,iOS系统在iOS7以后在NavigationBar的底部有一根一像素的线。需求就是修改这条线的颜色。


分析

第一想法就是找到NavigationBar里面这条线的视图,然后修改它的颜色。那么首先需要打印一下NavigationBar的UI结构。答应UI结构可以用[xxx.view recursiveDescription]在控制台输出UI层次关系。输出的关系如下(这里省略了一些不关心的字段)。

<UINavigationBar: 0x7ffe2a4780a0; frame = (0 20; 375 44); >

   | <_UINavigationBarBackground: 0x7ffe2a478800; frame = (0 -20; 375 64);>

   |    | <_UIBackdropView: 0x7ffe2a628d60; frame = (0 0; 375 64);>

   |    |    | <_UIBackdropEffectView: 0x7ffe2a62d660; frame = (0 0; 375 64); >

   |    |    | <UIView: 0x7ffe2a62ec10; frame = (0 0; 375 64); >

   |    | <UIImageView: 0x7ffe2a71f120; frame = (0 64; 375 0.5); >

   | <UINavigationItemView: 0x7ffe2a54ae00; frame = (164 8; 47 27);>

   |    | <UILabel: 0x7ffe2a54b7e0; frame = (0 3.5; 47 21.5); >

   | <_UINavigationBarBackIndicatorView: 0x7ffe2a4843c0; frame = (0 11.5; 13 21);>

可以看到在_UINavigationBarBackground这个View里面有一个高度为0.5的UIImageView。基本上可以确定应该就是这个视图了。那么如何获取这个视图呢。

  • 方案1:遍历NavigationBar的子视图。一层一层的找到_UINavigationBarBackground->UIImageView,然后取出UIImageView,设置这个视图的颜色。但这个方式有一个不不确定因素,如果将来UINavigationBar更新,_UINavigationBarBackground内部又增加了其他UIImageView就会出现问题。
  • 方案2:既然有这个视图在NavigationBar中,应该会有对应的属性声明在NavigationBar中,通过查看NavigationBar没有暴露的属性,发现有个_shadowView,打印内存地址,发现就是我们需要的视图。使用[view valueForKey:@"_shadowView"]就可以从获取到对应视图。所以采用方案2。

有了视图,就可以直接修改这个视图的属性了。iOS7可以,但是发现在iOS8上,不管修改这个UIImageView的背景颜色还是image。都不生效。那么这样的话,就创建一个视图,让这个视图覆盖在_shadowView上。在创建overlay视图的时候,也有2种方案。

  • 方案1:直接取_shadowView的frame,让它们的frame一样。然后添加到_UINavigationBarBackground这个图层中。可以Work,但是,有一个bug是在屏幕翻转的时候,NavigationBar的高度和_shadowView的位置都会变化。
  • 方案2:为了可以让我们自己的overlay可以任何时候都同_shadowView一样尺寸,这里使用AutoLayout,将overlay的上下左右约束设置成依赖_shadowView的上下左右边缘。这样,即使在屏幕翻转式,也会随着系统的样式变动。

实现

通过实验,我的基本实现思路是这的。

  1. 创建一个UINavigationBar的Category
  2. 通过runtime的AssociatedObject向Category添加一个属性用来存储创建的覆盖层。
  3. 提供ul_setUnderLineColor ul_reset2个方法。
  4. ul_setUnderLineColor方法用来从NavigationBar获取shadowView,并创建覆盖视图,设置覆盖视图的约束使其覆盖在shadowView上。
  5. ul_reset用来清除覆盖视图。

具体的实现代码可以在Github下载。

1 0
原创粉丝点击