Bartinter 閱讀筆記

簡(jiǎn)介

Bartinter 是一個(gè)關(guān)于 StatusBar 的庫,它的功能很簡(jiǎn)單也很實(shí)用:

Dynamically changes status bar style depending on content behind it

使用也簡(jiǎn)單:

  • Set "View controller-based status bar appearance" (UIViewControllerBasedStatusBarAppearance) to YES in your Info.plist.
  • Set ViewController's updatesStatusBarAppearanceAutomatically = true

原理

有兩個(gè)點(diǎn)

  • 一個(gè)是在需要的時(shí)候,計(jì)算狀態(tài)欄的亮度
  • 一個(gè)是利用 AOP 把上面的流程自動(dòng)化

計(jì)算狀態(tài)欄的亮度

注意 Bartinter 是一個(gè)繼承自 UIViewController 的類:public final class Bartinter: UIViewController,它以 childViewController 的形式獲得了父 VC 的生命周期狀態(tài)。

@objc public func refreshStatusBarStyle() 是更新狀態(tài)欄的核心函數(shù)。

其中,private func calculateStatusBarAreaAvgLuminance(_ completion: @escaping (CGFloat) -> Void) 方法,獲取父 VC 的 CALayer 和圖形上下文,計(jì)算狀態(tài)欄平均亮度,以決定 statusBarStyle。

注意這里有一個(gè)很貼心的細(xì)節(jié)是 antiFlickRange,用來防止亮度變更導(dǎo)致的狀態(tài)欄反復(fù)變化。如果沒有這一個(gè)細(xì)節(jié),雖然功能實(shí)現(xiàn)了,但是整體體驗(yàn)勢(shì)必要降低好幾個(gè)檔次。

@objc public func refreshStatusBarStyle() {
    calculateStatusBarAreaAvgLuminance { [weak self] avgLuminance in
        guard let strongSelf = self else { return }
        let antiFlick = strongSelf.configuration.antiFlickRange / 2
        if avgLuminance <= strongSelf.configuration.midPoint - antiFlick {
            strongSelf.statusBarStyle = .lightContent
        } else if avgLuminance >= strongSelf.configuration.midPoint + antiFlick {
            strongSelf.statusBarStyle = .default
        }
    }
}

流程自動(dòng)化

利用 AOP 把上面的流程自動(dòng)化。通過 hook 了UIViewController.childForStatusBarStyle,來返回 statusBarStyle。

設(shè)置則是通過 @IBInspectable var updatesStatusBarAppearanceAutomatically: Bool 這個(gè)入口,為 VC 附上一個(gè) Bartinter 的實(shí)例.

雖然示例里說手動(dòng)觸發(fā)舉的??是func scrollViewDidScroll(_ scrollView: UIScrollView),但實(shí)際上 AOP 的時(shí)候并沒有監(jiān)聽這個(gè)方法。

它監(jiān)聽的方法是:

public override func viewWillAppear(_ animated: Bool) // 通過 childViewController
public override func viewDidLayoutSubviews() // 通過 childViewController
UIView.layoutSubviews // 通過 AOP 堅(jiān)挺了 parent.view

這里還有一個(gè)很 tricky 的地方,在于 Bartinter 的 static func swizzleIfNeeded() 方法,因?yàn)槔锩娌]有做多線程保護(hù),所以第一想法就是擔(dān)心會(huì)產(chǎn)生多次 AOP 的問題。于是就著測(cè)試了一下,結(jié)果發(fā)現(xiàn)這個(gè)擔(dān)憂通過主線程得到了保護(hù):

Bartinter 的 static func swizzleIfNeeded() 方法只在初始化的時(shí)候調(diào)用,而如果我們?cè)诋惒骄€程去調(diào)用 init 方法,會(huì)觸發(fā) Apple 的錯(cuò)誤:“-[UIViewController initWithNibName:bundle:] must be used from main thread only”。通過主線程限制而規(guī)避了多線程競(jìng)爭(zhēng)問題。

并且它是 internal 的,外部不可訪問,防止了開發(fā)者的濫用。

static func swizzleIfNeeded() {
    guard isSwizzlingEnabled && !isSwizzlingPerformed else { return }
    UIViewController.setupChildViewControllerForStatusBarStyleSwizzling()
    UIView.setupSetNeedsLayoutSwizzling()
    isSwizzlingPerformed = true
}

public init(_ configuration: Configuration = Configuration()) {
    self.configuration = configuration
    Bartinter.swizzleIfNeeded()
    super.init(nibName: nil, bundle: nil)
}

Throttler

這個(gè) Throttler 類挺有意思,可以直接用。Throttle 作為一個(gè)很實(shí)用的功能,在 Rx 里面也有集成。

這個(gè) Throttler 采用的是首次立即實(shí)行,后續(xù)延遲執(zhí)行的方案,保證一個(gè)maxInterval 內(nèi)最多只會(huì)執(zhí)行一次。在一直調(diào)用的情況下最壞下次執(zhí)行需要間隔 (2 * maxInterval - 1最小時(shí)間單位)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容