iOS適配深色DarkMode模式

iOS在13的版本加入了對深色模式的支持承二,深色模式下App整體上呈現(xiàn)黑色UI界面,現(xiàn)在許多App都完成了深色模式的適配侣诵,但也有少量App未支持深色模式(這些App大多是內嵌較多的H5頁面)例如支付寶等树绩,本文主要介紹iOS深色模式的適配峦筒。

System Colors

Apple為了適配深色模式對UIKit中的UIColor進行了重新定義氏义,例如將.red, .blue.yellow定義為.systemRed锄列,.systemBlue.systemYellow,這些新定義的System Colors在深色和淺色模式下表現(xiàn)為不同的顏色惯悠,具體的色值可以見蘋果的官方文檔邻邮。

Semantic Colors

對于一些需要進行文字顯示的控件Apple也做了深色模式的適配,Apple新加了Semantic Colors顏色方案克婶,使用Semantic Colors時不需要糾結具體的值筒严,只需要在合適的場景使用丹泉,例如當控件是Label時,在沒有自定義字體顏色時鸭蛙,可以使用.label類型的的Semantic Colors摹恨,在淺色模式下顯示黑色字體,在深色模式下顯示白色字體娶视;Semantic Colors包括.label晒哄,.separator.link歇万, .systemBackground.systemFill揩晴。

對于系統(tǒng)適配的以上深色模式下的顏色可以使用SemanticUI這個App進行查看勋陪,該App給出了二種模式下的系統(tǒng)顏色的對比:

Dynamic Colors

當然在實際開發(fā)中很多情況下我們都是需要自定義顏色的贪磺,有幸的是Apple也給出了相應的方案,那就是通過UIColor.init(dynamicProvider: @escaping (UITraitCollection) -> UIColor)這個方法進行創(chuàng)建自定義的semantic color诅愚。

使用代碼的方式:

import UIKit
infix operator |: AdditionPrecedence
public extension UIColor {
    static func | (lightMode: UIColor, darkMode: UIColor) -> UIColor {
        guard #available(iOS 13.0) else { return lightMode }          
        return UIColor { (traitCollection) -> UIColor in
            return traitCollection.userInterfaceStyle == .light ? lightMode : darkMode
        }
    }
}
// 使用
let dynamicColor = .black | .white

使用Asset Catalog方式

自動iOS11開始寒锚,可以在Asset Catalogs保存自定義顏色,并支持Interface Buildercode二種方式使用违孝,自定義的color目前也支持深色模式刹前。打開Assets.xcassets ,店家左下角的+號按鈕新增一個Color Set雌桑,在Any Appearence(淺色模式)和Dark Appearence(深色模式)分別添加一種顏色即可喇喉。


當然也支持代碼的使用:

Let view =  Uiview()
View.backdroundcolor = Color(named: Color)

Border colors

Border colors在當主題模式發(fā)生改變時并不會自動的進行適配,所以需要手動的進行處理校坑,可以通過traitCollectionDidChange(_:)這個方法在進行處理:

import UIKit
extension UIColor {
    static let layer = UIColor(light: .init(red: 106 / 255, green: 32 / 255, blue: 119 / 255, alpha: 1),
                               dark: .init(red: 138 / 255, green: 76 / 255, blue: 146 / 255, alpha: 1))
}
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    traitCollection.hasDifferentColorAppearance(comparedTo: traitCollection) {
        layer.backgroundColor = UIColor.layer.cgColor
    }
}

引起TraitCollection變化有多種情況的拣技,不只是系統(tǒng)顯示模式的切換,例如當屏幕方向發(fā)生旋轉時也會觸發(fā)上面的方法耍目,所以最好是做一個判斷膏斤,當TraitCollection的更改影響color的值時才給layer賦值,在Xcode中可以添加一個launch argument:UITraitCollertionChangeLoggingEnabled = YES來檢測TraitCollection的改變:

Dynamic Images

圖片資源同樣支持深色模式邪驮,需要使用Assets.xcassets莫辨,新建一個Assets.xcassets并在Attributes inspector點擊Appearances選擇Any, Dark,然后分別為Any AppearancesDark Appearances配置響應的圖片毅访。


實際開發(fā)中圖片資源可能是后臺返回的沮榜,這時候需要使用image assets

import UIKit
public extension UIImageAsset {
    convenience init(lightModeImage: UIImage?, darkModeImage: UIImage?) {
        self.init()
        register(lightModeImage: lightModeImage, darkModeImage: darkModeImage)
    }
    func register(lightModeImage: UIImage?, darkModeImage: UIImage?) {
        register(lightModeImage, for: .light)
        register(darkModeImage, for: .dark)
    }
    func register(_ image: UIImage?, for traitCollection: UITraitCollection) {
        guard let image = image else {
            return
        }
        register(image, with: traitCollection)
    }
    func image() -> UIImage {
        if #available(iOS 13.0, tvOS 13.0, *) {
            return image(with: .current)
        }
        return image(with: .light)
    }
}

盡量減少圖片

為了適配深色模式,無限的增加圖片資源最終會導致包的大小會增加很多喻粹,為了減少包的體積蟆融,在非必要添加圖片的情況下有以下二種方案:

  • 使用tint color
    對于那些像toolbarstab bars的圖片,可以用tint color去渲染這些圖片以滿足深色模式磷斧,首先需要讓圖片以模板的形式渲染:

或者:

let iconImage = UIImage()
let imageView = UIImageView()
imageView.image = iconImage.withRenderingMode(.alwaysTemplate)

使用:

public static var tint: UIColor = {
    if #available(iOS 13, *) {
        return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in
            if UITraitCollection.userInterfaceStyle == .dark {
                return Colors.osloGray
            } else {
                return Colors.dataRock
            }
        }
    } else {
        return Colors.dataRock
    }
}()
imageView.tintColor = Style.Colors.tint
  • 反轉圖片顏色
    反轉圖片顏色也是一種好的選擇振愿,只是不是對所有的圖片都合適捷犹,可以使用如下代碼:
extension UIImage {
    func invertedColors() -> UIImage? {
        guard let ciImage = CIImage(image: self) ?? ciImage, let filter = CIFilter(name: "CIColorInvert") else { return nil }
        filter.setValue(ciImage, forKey: kCIInputImageKey)
        guard let outputImage = filter.outputImage else { return nil }
        return UIImage(ciImage: outputImage)
    }
}

自動更新 Dark Mode

現(xiàn)在支持深色模式的App都會提供淺色和深色模式的選擇,同時支持跟誰系統(tǒng)自動切換冕末,當用戶選擇相應模式后需要用Userdefaults保存用戶的選擇萍歉。

import UIKit
public extension UserDefaults {
    var overridedUserInterfaceStyle: UIUserInterfaceStyle {
        get {
            UIUserInterfaceStyle(rawValue: integer(forKey: #function)) ?? .unspecified
        }
        set {
            set(newValue.rawValue, forKey: #function)
        }
    }
}

當保存了用戶選擇后同時需要更新當前App的顯示:

public extension UIApplication {
    func override(_ userInterfaceStyle: UIUserInterfaceStyle) {
        // iPad支持多窗口,不支持iPad的話可以刪除這段判斷
        if #available(iOS 13.0, *), supportsMultipleScenes {
            for connectedScene in connectedScenes {
                if let scene = connectedScene as? UIWindowScene {
                    scene.windows.override(userInterfaceStyle)
                }
            }
        }
        else {
            windows.override(userInterfaceStyle)
        }
    }
}
public extension UIWindow {
    func override(_ userInterfaceStyle: UIUserInterfaceStyle) {
        if #available(iOS 13.0, tvOS 13.0, *) {
            overrideUserInterfaceStyle = userInterfaceStyle
        }
    }
}
public extension Array where Element: UIWindow {
    func override(_ userInterfaceStyle: UIUserInterfaceStyle) {
        for window in self {
            window.override(userInterfaceStyle)
        }
    }
}
    UIApplication.shared.override(style)
    UserDefaults.standard.overridedUserInterfaceStyle = style

退出或禁用深色模式

當采用Xcode 11新建項目時會默認開啟深色模式的档桃,當沒時間或者暫不支持深色模式時可以禁用掉枪孩,簡單的方法時在Info.plist中添加一個UIUserInterfaceStyle = Light,也支持為某個View或者ViewController單獨設置相應的模式藻肄,但是這種設置不會影響模態(tài)彈出的VC的模式蔑舞。

let view = UIView()
view.overrideUserInterfaceStyle = .dark
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        overrideUserInterfaceStyle = .dark
    }
}

總結:

本文詳細介紹了iOS深色模式的適配,最主要的是通過UITraitCollection來拿到當前的樣式來進行相應的顏色上的適配嘹屯,Apple也提供的新定義的幾種系統(tǒng)顏色方案并能自動適配深色模式攻询,深色模式適配代碼不多,就是需要將項目所有的界面都要進行適配州弟,工作量巨大钧栖。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市婆翔,隨后出現(xiàn)的幾起案子拯杠,更是在濱河造成了極大的恐慌,老刑警劉巖啃奴,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件潭陪,死亡現(xiàn)場離奇詭異,居然都是意外死亡最蕾,警方通過查閱死者的電腦和手機依溯,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來揖膜,“玉大人誓沸,你說我怎么就攤上這事∫妓冢” “怎么了拜隧?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長趁仙。 經(jīng)常有香客問我洪添,道長,這世上最難降的妖魔是什么雀费? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任干奢,我火速辦了婚禮,結果婚禮上盏袄,老公的妹妹穿的比我還像新娘忿峻。我一直安慰自己薄啥,他們只是感情好,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布逛尚。 她就那樣靜靜地躺著垄惧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绰寞。 梳的紋絲不亂的頭發(fā)上到逊,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天,我揣著相機與錄音滤钱,去河邊找鬼觉壶。 笑死,一個胖子當著我的面吹牛件缸,可吹牛的內容都是我干的铜靶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼停团,長吁一口氣:“原來是場噩夢啊……” “哼旷坦!你這毒婦竟也來了?” 一聲冷哼從身側響起佑稠,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎旗芬,沒想到半個月后舌胶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡疮丛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年幔嫂,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片誊薄。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡履恩,死狀恐怖,靈堂內的尸體忽然破棺而出呢蔫,到底是詐尸還是另有隱情切心,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布片吊,位于F島的核電站绽昏,受9級特大地震影響,放射性物質發(fā)生泄漏俏脊。R本人自食惡果不足惜全谤,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望爷贫。 院中可真熱鬧认然,春花似錦补憾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至子刮,卻和暖如春威酒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挺峡。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工葵孤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人橱赠。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓尤仍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親狭姨。 傳聞我的和親對象是個殘疾皇子宰啦,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內容