iOS 导航栏的那些事儿

最近项目里有个需求和导航栏的样式定制有关,深入之后发现之前理解的一些概念有些模糊,刚好趁着这次机会全面整理了一下。

从 iOS7 开始,苹果采用了大量的扁平化和毛玻璃风格,刚升级到 iOS7 之后会发现界面的布局多多少少有一些偏差(当然现在新建的项目没有这方面困扰,不需要经历6到7的适配),适配过程中会发现如下一些属性,

- edgesForExtendedLayout
- translucent
- extendedLayoutIncludesOpaqueBars
- automaticallyAdjustsScrollViewInsets

根据字面意思看上去这些属性很好理解,但是发现他们组合之后会有一些不同的表现,一些奇怪的问题也不知道什么原因导致的。不用担心,接下去我会全面的解析一下这几个属性的含义,保证你再也不怕各种奇怪的导航栏问题啦。

edgesForExtendedLayout + translucent

iOS7 以后,edgesForExtendedLayout 的默认设置是 UIRectEdgeAll,translucent 的默认值是 true。这种组合会使 rootView 的布局从(0,0)开始,即 view 的内容会被导航栏遮挡住,大多数情况下将 edgesForExtendedLayout 修改为 UIRectEdgeNone 就能解决布局被遮挡的问题。将 translucent 设置成 false 也会使 rootView 从导航栏底部开始,但是 translucent = false 时即使将 edgesForExtendedLayout 再改成 UIRectEdgeAll rootView 还是从导航栏底部开始布局。如何可以在导航栏不透明的情况下让 rootView 从(0,0)开始布局呢?苹果也考虑到了这种需求,提供了 extendedLayoutIncludesOpaqueBars 这个属性。

小结:translucent 为 true,rootView 从(0,0)开始布局,修改 edgesForExtendedLayout 属性可以改变布局;translucent 为 false,rootView 从导航栏底部开始布局,修改 edgesForExtendedLayout 属性无法改变布局。

extendedLayoutIncludesOpaqueBars + translucent

前面我们知道了 translucent 为 false 时,修改 edgesForExtendedLayout 也无法使 rootView 从(0,0)开始布局。苹果为此提供了 extendedLayoutIncludesOpaqueBars,字面上理解的意思就是在不透明的导航栏下也全屏显示。

这里多提一点,在 ViewController 的生命周期中有 viewDidLoad,viewWillAppear,viewDidAppear,viewWillDisappear,viewDidDisappear,上述提到的这些属性需要在 viewDidAppear 之前设置好,viewDidAppear 可以认为系统已经根据配置布局好了,在这里展示给用户看。

automaticallyAdjustsScrollViewInsets

automaticallyAdjustsScrollViewInsets 默认值是 true,表示在全屏模式下会自动修改第一个添加到 rootView 的 scrollview 的 contentInset 为(64,0,0,0),这样 scrollview 就不会被导航栏遮挡了。

关于 scrollview 有一个问题比较常见,这里解析一下原因。我们经常会这么使用一个 tableView,

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    self.view.backgroundColor = [UIColor greenColor];
    
    self.navigationItem.title = @"Master";
    self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];
    _tableView.backgroundColor = [UIColor whiteColor];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    [self.view addSubview:_tableView];
}

这样在默认情况下(translucent = true, edgesForExtendedLayout = UIRectEdgeAll),tableView的显示没有问题。但是当我们将 edgesForExtendedLayout 设置成 UIRectEdgeNone 时,当 tableView 的内容比较多时底部的内容反而显示不下。这就很奇怪了,按照前面的结论,这时候 tableView是从导航栏底部开始布局的,contentInset 也是(0,0,0,0),怎么底部的内容会被遮挡一部分呢?原因在于 self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds]; 初始化时 rootView 的 frame 还是(0,0,screenWidth,screenHeight),只需要在 viewWillLayoutSubviews 中重新修改一下 tableview 的 frame 即可,

- (void)viewWillLayoutSubviews
{
    [super viewWillLayoutSubviews];
    _tableView.frame = self.view.bounds;
}

UINavigationBar 修改背景色

UINavigationBarUIView 的子类,首先想到的是修改背景色,
self.navigationController.navigationBar.backgroundColor = [UIColor greenColor];

发现这并不是我们想要的效果,为什么绿色变淡了呢?通过 Xcode 的 ViewDebugging 我们可以看到 UINavigationBar 内部还有一些子视图,这些子视图的背景色会遮挡住我们设置的颜色。

查看 UINavigationBar 的接口我们发现 setBackgroundImage,设置

[self.navigationController.navigationBar setBackgroundImage:[UIImage imageWithColor:[UIColor greenColor] size:CGSizeMake(1, 1)] forBarMetrics:UIBarMetricsDefault];

结果如下

小结:设置 UINavigationBar 的 backgroundImage 可以修改导航栏的背景色。

translucent 和 setBackgroundImage

前面提到我们可以通过修改背景图片来修改导航栏的背景色,设置了背景图片后在有些页面我们会遇到一些奇怪的问题,发现原来布局正常的页面显示不对了,会多出一部分空白或者被导航栏遮挡住了。

通过打印出 translucent 的值我们发现设置了纯色的背景图后原来半透明的导航栏变成了不透明的,结合前面提到的 translucent 对布局起点的影响,如果页面是按照半透明情况,即 rootView 从(0,0)开始布局来设置子视图的 frame,那么设置了纯色背景图后 translucent 变成了 false,即 rootView 从(0,64)开始布局。为什么设置背景图片会影响 translucent 呢,通过查看文档发现了如下说明,

/*
 New behavior on iOS 7.
 Default is YES.
 You may force an opaque background by setting the property to NO.
 If the navigation bar has a custom background image, the default is inferred 
 from the alpha values of the image—YES if it has any pixel with alpha < 1.0
 If you send setTranslucent:YES to a bar with an opaque custom background image
 it will apply a system opacity less than 1.0 to the image.
 If you send setTranslucent:NO to a bar with a translucent custom background image
 it will provide an opaque background for the image using the bar's barTintColor if defined, or black
 for UIBarStyleBlack or white for UIBarStyleDefault if barTintColor is nil.
 */
 @property(nonatomic,assign,getter=isTranslucent) BOOL translucent NS_AVAILABLE_IOS(3_0) UI_APPEARANCE_SELECTOR; // Default is NO on iOS 6 and earlier. Always YES if barStyle is set to UIBarStyleBlackTranslucent

也就是说背景图片如果包含 alpha 的色值,系统会默认将 translucent 设置为 true,没有包含 alpha 色值会将 translucent 设置为 false。这下真相大白了,原来我们前面设置了纯绿色的背景图片,是不包含 alpha 色值的,即系统默认将 translucent 设置成了 false。但这是针对没有手动设置 translucent 值的情况,如果我们手动设置了 translucent,那么系统就不会根据背景图片的 alpha 来修改 translucent

至此,我们了解了苹果是如何使用这几个属性的,针对 iOS7 以上,这里做一下总结:

  1. iOS7 以后 translucent 默认为 true,rootView 从(0,0)开始布局,修改 edgesForExtendedLayout 属性可以改变布局;
  2. translucent 为 false,rootView 从导航栏底部开始布局,修改 edgesForExtendedLayout 属性无法改变布局,可以通过设置 extendedLayoutIncludesOpaqueBars 从(0,0)开始布局;
  3. automaticallyAdjustsScrollViewInsets 默认值是 true,表示在全屏模式下会自动修改第一个添加到 rootView 的 scrollview 的 contentInset 为(64,0,0,0),用来纠正scrollview在全屏模式下的显示;
  4. 设置 UINavigationBar 的背景图片可以改变导航栏背景色,如果背景图片包含 alpha 的色值,系统会默认将 translucent 设置为 true,没有包含 alpha 色值会将 translucent 设置为 false。但这是针对没有手动设置 translucent 值的情况,如果我们手动设置了 translucent,那么系统就不会根据背景图片的 alpha 来修改 translucent

欢迎大家关注我们团队的公众号,不定期分享各类技术干货


image
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容