UIStatusBarStyle 解惑

所有文章已搬迁到个人站点:me.harley-xk.studio,欢迎访问留言

作为一个强迫症晚期患者,对于 StatusBar 这样的细节也是无法放过的。对于每一个界面,StatusBar 都必须显示成需要的正确的风格,为此呕心沥血,殚精竭虑。。。而后有了这么一篇总结。

原始手段

要实现对 StatusBar Style 的绝对控制,其实有一个最简单粗暴的方法:

open func setStatusBarStyle(_ statusBarStyle: UIStatusBarStyle, animated: Bool)

这是从 iOS 2.0 那个古老的年代就已经存在的方法了,通过它可以直接设置 StatusBar 的 Style。

不过苹果一直在不遗余力地废弃老旧的 API,很不幸的,iOS 9 之后,这个方法也被标记为 deprecated 了:

@available(iOS, introduced: 2.0, deprecated: 9.0, message: "Use -[UIViewController preferredStatusBarStyle]")
open func setStatusBarStyle(_ statusBarStyle: UIStatusBarStyle, animated: Bool)

而且如果想要继续使用这个方式来管理 StatusBar Style 的话,还需要在 Info.plist 文件中设置:View controller-based status bar appearanceNO

但是代码里面满篇的 deprecated 警告以及 Info.plist 中额外的设置项对于一个强迫症晚期患者是无法容忍的;更重要的是, deprecated 意味着不知道将来的哪一天这个 API 就消失了,埋这样一个雷在 App 里面实在是一件很坑的事情,所以这条路被无情地堵死了。

新的方向

其实 setStatusBarStyle 方法被废除也是意料之中的,如今编程思想已经在不知不觉中进步了不少,远古时代这种面向实现的编程思想已经与现如今的协议式编程相去甚远了。虽然老方法能够简单粗暴地解决问题,但是同样也留下了许多后遗症,比如在太过复杂的逻辑中,直接设置 StatusBar 的 Style 往往会造成混乱,最终得不到我们想要的效果;另外,由于我们可以在任何地方、任何代码逻辑中改变 StatusBar 的 Style,这对后期维护来说往往是灾难性的,最终导致得不偿失。

其实苹果早在 iOS 7 中就与时俱进地提供了新的控制 Status Bar Style 的体系,也就是上面的 View controller-based status bar appearance,通过限制改变 Status Bar Style 的自由性,转而交由 View Controller 去负责自己生命周期中的 Status Bar Style 控制。这么一来,整个思路都变得更加清晰和优雅了。

基本方式

在新的体系下工作其实更简单了,只需要在 ViewController 中重写相关属性即可:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
}

App 在运行过程中,始终由在 UI 最顶端的 ViewController 的 preferredStatusBarStyle 属性来决定当前 Status Bar 的 Style。当我们需要更改 Style 时,只需要在 preferredStatusBarStyle 计算方法中返回新的 style 属性值,然后调用 setNeedsStatusBarAppearanceUpdate() 方法即可:

override var preferredStatusBarStyle: UIStatusBarStyle {
    if needsLightContent {
        return .lightContent
    } else {
        return .default
    }
}
    
private func changeStyle() {
    needsLightContent = true
    setNeedsStatusBarAppearanceUpdate()
}

通过这种模式,我们始终通过 ViewController 来管理 Status Bar 的 Style,就算需要更新它的状态,也是通过间接的方式通知到 ViewController ,而不是直接改变 Status Bar 的属性。这样,后期的代码维护工作也可以更加轻松了。

分发控制权限

如果顶部的 ViewController 只是一个空壳,实际的 UI 逻辑都是由添加到之上的 ChildViewController 来控制的,那么这时候你可以通过重写 childViewControllerForStatusBarStyle 属性,将 Status Bar Style 的控制权分发到对应的ChildViewController 中去:

override var childViewControllerForStatusBarStyle: UIViewController? {
    let currentChildViewController = childViewControllers[0]
    return currentChildViewController
}

特殊情况

当然,新的模式也引入了新的规则(keng),我们在体会新的简单粗暴的手段时,也遇到了新的问题:如果不同的 ViewController 争夺控制权怎么办?比如 NavigationModal Presentation 这两个另类,他们都是一个 ViewController 嵌套在另一个 ViewController 的典型代表,这时候到底是谁说了算?

有坑就得填,首先拿 Navigation 开刀。

Navigation

当 UI 最顶层的 ViewController 嵌套在 UINavigationController 中时,UINavigationController 和 栈顶的 ContentViewController 都想争夺 Status Bar 的控制权,如果不加以控制,那刚刚建立的新秩序立刻就要被破坏了,于是我们有了下面的规则:

  • Navigation Bar 不可见时,由栈顶的 ContentViewController 负责管理 Status Bar 的 Style

  • Navigation Bar 可见时,由 NavigationController 负责管理 Status Bar 的 Style

    这时候,由于 Navigation Bar 处于可视状态,因此 Status Bar 需要与 Navigation Bar 的风格保持一致,因此由 NavigationController 来控制 Status Bar Style 是最合理的。

    但是这时候同样存在新的问题:NavigationController 往往都是使用系统默认的 UINavigationController 类,如果这时候的 Status Bar Style 不符合我们的需求怎么办?如果不继承 UINavigationController 就没法重写 preferredStatusBarStyle 属性;如果继承了 UINavigationController ,虽然可以实现需求,但是一来破坏了代码的简洁性,并且苹果是不建议继承 UINavigationController 的,二来有许多系统组件是直接继承自 UINavigationController 的(比如 MFMailComposeViewController),这时候继承也只是然并卵了。那么有什么办法可以解决这个问题呢?

必须有,苹果早就考虑到了这一点,于是针对 UINavigationBar 做了一番工作,提供了设置 Status Bar Style 的入口:barStyle 属性。barStyle 属性决定了 NavigationBar 的外观,因此,修改 barStyle 属性会联动改变 Status Bar 的 Style:

  • barStyle = .default 时,NavigationBar 显示为黑色,此时 StatusBar 显示为白色

  • barStyle = .black 时,NavigationBar 显示为白色,此时 StatusBar 显示为黑色

    所以,如果通过设置 barTintColor 自定义了 NavigationBar 的颜色,这时候就需要设置 barStyle 属性来告知 NavigationController 正确的 Status Bar Style。

    另外需要注意的是,由于某种原因,设置 barStyle 后会改变 barTintColor 的值,因此需要先修改 barStyle 属性,然后再设置 barTintColor

Modal Presentation

在了解了 Navigation 之后,Model Presentation 的逻辑就相对简单了,通过 Modal Presentation 呈现的视图,如果也是嵌套在 NavigationController 中的话,此时的规则和 Navigaion 中描述的一致,否则的话同样由最顶端的 ViewController 来决定 Status Bar 的 Style。

需要注意的是,Modal Presentation 的逻辑有一个 >>前提条件<< ,只有当 modalPresentationStyle 的属性为 .fullScreen ,也就是全屏 Presentation 时,呈现出的视图才对 Status Bar Style 有发言权,否则还是由原先的 ViewController 来控制。

但是,凡事都有个但是不是么,如果你的强迫症已经无药可救了,非要在不全屏 Presentation 的 ViewController 中控制 Status Bar 的 Style,其实也不是不可以,modalPresentationCapturesStatusBarAppearance 是专门为强迫症患者量身定制的,只需要重写这个属性并返回 true ,你就可以在任何形式的 Model Presentation 中获得对 Status Bar Style 的控制权了。

结语

UIStatusBar 是大多数 App 都需要接触到的,但是往往也是最容易被忽视的一个细节,我们见过太多状态栏显示异常的 App (也包括某些大公司的产品)。很多时候这个代表了一个人、一个团队做事、做产品的态度。我一贯认为应该以对待一个艺术品而不是商品生产线的态度去开发软件,任何的细节都不应该放过。与其整天高谈阔论所谓的算法、性能,倒不如先从手头的细节做起,完善每一处用户体验,杜绝劣质 App。

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

推荐阅读更多精彩内容

  • 本文为大地瓜原创,欢迎知识共享,转载请注明出处。虽然你不注明出处我也没什么精力和你计较。作者微信号:christg...
    大地瓜123阅读 709评论 0 0
  • 本文为大地瓜原创,欢迎知识共享,转载请注明出处。虽然你不注明出处我也没什么精力和你计较。作者微信号:christg...
    大地瓜123阅读 861评论 1 2
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,010评论 4 62
  • 张学姐阅读 403评论 0 0
  • 有机会陪伴于左右的时候,就不要只去想念一个人。 飞机、轮船、火车,每一种交通方式,对于现代人来说,回家——都不是问...
    君子包阅读 265评论 0 3