所有文章已搬遷到個人站點: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 appearance
為 NO
。
但是代碼里面滿篇的 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)怎么辦?比如 Navigation
和 Modal 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。