UIStatusBarStyle 解惑

所有文章已搬遷到個人站點:me.harley-xk.studio,歡迎訪問留言

作為一個強迫癥晚期患者,對于 StatusBar 這樣的細節(jié)也是無法放過的。對于每一個界面,StatusBar 都必須顯示成需要的正確的風(fēng)格,為此嘔心瀝血,殫精竭慮。。。而后有了這么一篇總結(jié)。

原始手段

要實現(xiàn)對 StatusBar Style 的絕對控制,其實有一個最簡單粗暴的方法:

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

這是從 iOS 2.0 那個古老的年代就已經(jīng)存在的方法了,通過它可以直接設(shè)置 StatusBar 的 Style。

不過蘋果一直在不遺余力地廢棄老舊的 API,很不幸的,iOS 9 之后,這個方法也被標(biāo)記為 deprecated 了:

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

而且如果想要繼續(xù)使用這個方式來管理 StatusBar Style 的話,還需要在 Info.plist 文件中設(shè)置:View controller-based status bar appearanceNO

但是代碼里面滿篇的 deprecated 警告以及 Info.plist 中額外的設(shè)置項對于一個強迫癥晚期患者是無法容忍的;更重要的是, deprecated 意味著不知道將來的哪一天這個 API 就消失了,埋這樣一個雷在 App 里面實在是一件很坑的事情,所以這條路被無情地堵死了。

新的方向

其實 setStatusBarStyle 方法被廢除也是意料之中的,如今編程思想已經(jīng)在不知不覺中進步了不少,遠古時代這種面向?qū)崿F(xiàn)的編程思想已經(jīng)與現(xiàn)如今的協(xié)議式編程相去甚遠了。雖然老方法能夠簡單粗暴地解決問題,但是同樣也留下了許多后遺癥,比如在太過復(fù)雜的邏輯中,直接設(shè)置 StatusBar 的 Style 往往會造成混亂,最終得不到我們想要的效果;另外,由于我們可以在任何地方、任何代碼邏輯中改變 StatusBar 的 Style,這對后期維護來說往往是災(zāi)難性的,最終導(dǎo)致得不償失。

其實蘋果早在 iOS 7 中就與時俱進地提供了新的控制 Status Bar Style 的體系,也就是上面的 View controller-based status bar appearance,通過限制改變 Status Bar Style 的自由性,轉(zhuǎn)而交由 View Controller 去負責(zé)自己生命周期中的 Status Bar Style 控制。這么一來,整個思路都變得更加清晰和優(yōu)雅了。

基本方式

在新的體系下工作其實更簡單了,只需要在 ViewController 中重寫相關(guān)屬性即可:

override var preferredStatusBarStyle: UIStatusBarStyle {
    return .lightContent
}

App 在運行過程中,始終由在 UI 最頂端的 ViewController 的 preferredStatusBarStyle 屬性來決定當(dāng)前 Status Bar 的 Style。當(dāng)我們需要更改 Style 時,只需要在 preferredStatusBarStyle 計算方法中返回新的 style 屬性值,然后調(diào)用 setNeedsStatusBarAppearanceUpdate() 方法即可:

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

通過這種模式,我們始終通過 ViewController 來管理 Status Bar 的 Style,就算需要更新它的狀態(tài),也是通過間接的方式通知到 ViewController ,而不是直接改變 Status Bar 的屬性。這樣,后期的代碼維護工作也可以更加輕松了。

分發(fā)控制權(quán)限

如果頂部的 ViewController 只是一個空殼,實際的 UI 邏輯都是由添加到之上的 ChildViewController 來控制的,那么這時候你可以通過重寫 childViewControllerForStatusBarStyle 屬性,將 Status Bar Style 的控制權(quán)分發(fā)到對應(yīng)的ChildViewController 中去:

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

特殊情況

當(dāng)然,新的模式也引入了新的規(guī)則(keng),我們在體會新的簡單粗暴的手段時,也遇到了新的問題:如果不同的 ViewController 爭奪控制權(quán)怎么辦?比如 NavigationModal Presentation 這兩個另類,他們都是一個 ViewController 嵌套在另一個 ViewController 的典型代表,這時候到底是誰說了算?

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

Navigation

當(dāng) UI 最頂層的 ViewController 嵌套在 UINavigationController 中時,UINavigationController 和 棧頂?shù)?ContentViewController 都想爭奪 Status Bar 的控制權(quán),如果不加以控制,那剛剛建立的新秩序立刻就要被破壞了,于是我們有了下面的規(guī)則:

  • 當(dāng) Navigation Bar 不可見時,由棧頂?shù)?ContentViewController 負責(zé)管理 Status Bar 的 Style

  • 當(dāng) Navigation Bar 可見時,由 NavigationController 負責(zé)管理 Status Bar 的 Style

    這時候,由于 Navigation Bar 處于可視狀態(tài),因此 Status Bar 需要與 Navigation Bar 的風(fēng)格保持一致,因此由 NavigationController 來控制 Status Bar Style 是最合理的。

    但是這時候同樣存在新的問題:NavigationController 往往都是使用系統(tǒng)默認的 UINavigationController 類,如果這時候的 Status Bar Style 不符合我們的需求怎么辦?如果不繼承 UINavigationController 就沒法重寫 preferredStatusBarStyle 屬性;如果繼承了 UINavigationController ,雖然可以實現(xiàn)需求,但是一來破壞了代碼的簡潔性,并且蘋果是不建議繼承 UINavigationController 的,二來有許多系統(tǒng)組件是直接繼承自 UINavigationController 的(比如 MFMailComposeViewController),這時候繼承也只是然并卵了。那么有什么辦法可以解決這個問題呢?

必須有,蘋果早就考慮到了這一點,于是針對 UINavigationBar 做了一番工作,提供了設(shè)置 Status Bar Style 的入口:barStyle 屬性。barStyle 屬性決定了 NavigationBar 的外觀,因此,修改 barStyle 屬性會聯(lián)動改變 Status Bar 的 Style:

  • 當(dāng) barStyle = .default 時,NavigationBar 顯示為黑色,此時 StatusBar 顯示為白色

  • 當(dāng) barStyle = .black 時,NavigationBar 顯示為白色,此時 StatusBar 顯示為黑色

    所以,如果通過設(shè)置 barTintColor 自定義了 NavigationBar 的顏色,這時候就需要設(shè)置 barStyle 屬性來告知 NavigationController 正確的 Status Bar Style。

    另外需要注意的是,由于某種原因,設(shè)置 barStyle 后會改變 barTintColor 的值,因此需要先修改 barStyle 屬性,然后再設(shè)置 barTintColor

Modal Presentation

在了解了 Navigation 之后,Model Presentation 的邏輯就相對簡單了,通過 Modal Presentation 呈現(xiàn)的視圖,如果也是嵌套在 NavigationController 中的話,此時的規(guī)則和 Navigaion 中描述的一致,否則的話同樣由最頂端的 ViewController 來決定 Status Bar 的 Style。

需要注意的是,Modal Presentation 的邏輯有一個 >>前提條件<< ,只有當(dāng) modalPresentationStyle 的屬性為 .fullScreen ,也就是全屏 Presentation 時,呈現(xiàn)出的視圖才對 Status Bar Style 有發(fā)言權(quán),否則還是由原先的 ViewController 來控制。

但是,凡事都有個但是不是么,如果你的強迫癥已經(jīng)無藥可救了,非要在不全屏 Presentation 的 ViewController 中控制 Status Bar 的 Style,其實也不是不可以,modalPresentationCapturesStatusBarAppearance 是專門為強迫癥患者量身定制的,只需要重寫這個屬性并返回 true ,你就可以在任何形式的 Model Presentation 中獲得對 Status Bar Style 的控制權(quán)了。

結(jié)語

UIStatusBar 是大多數(shù) App 都需要接觸到的,但是往往也是最容易被忽視的一個細節(jié),我們見過太多狀態(tài)欄顯示異常的 App (也包括某些大公司的產(chǎn)品)。很多時候這個代表了一個人、一個團隊做事、做產(chǎn)品的態(tài)度。我一貫認為應(yīng)該以對待一個藝術(shù)品而不是商品生產(chǎn)線的態(tài)度去開發(fā)軟件,任何的細節(jié)都不應(yīng)該放過。與其整天高談闊論所謂的算法、性能,倒不如先從手頭的細節(jié)做起,完善每一處用戶體驗,杜絕劣質(zhì) App。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 本文為大地瓜原創(chuàng),歡迎知識共享,轉(zhuǎn)載請注明出處。雖然你不注明出處我也沒什么精力和你計較。作者微信號:christg...
    大地瓜123閱讀 743評論 0 0
  • 本文為大地瓜原創(chuàng),歡迎知識共享,轉(zhuǎn)載請注明出處。雖然你不注明出處我也沒什么精力和你計較。作者微信號:christg...
    大地瓜123閱讀 910評論 1 2
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,241評論 4 61
  • 張學(xué)姐閱讀 456評論 0 0
  • 有機會陪伴于左右的時候,就不要只去想念一個人。 飛機、輪船、火車,每一種交通方式,對于現(xiàn)代人來說,回家——都不是問...
    君子包閱讀 281評論 0 3