iOS13 (五)暗黑模式Dark Mode

最近公司業(yè)務(wù)需求要更換APP主題。最開始是一個(gè)地方一個(gè)地方去改氧映,而且項(xiàng)目中很多老代碼是用xib寫的窄俏,習(xí)慣純代碼編程的我改的很難受。而且以后指不定要再次更改主題城须。

于是我定義了幾個(gè)主要顏色的宏,代碼中只要是設(shè)置顏色的地方就用宏米苹。這樣只需要改一次糕伐,當(dāng)要切換主題的時(shí)候直接對(duì)宏進(jìn)行更改就行了。

結(jié)合已做好的切換主題功能蘸嘶,再加上一個(gè)暗黑模式判斷良瞧,如果當(dāng)前是暗黑模式就用A套色值陪汽,如果不是就用B套色值,這樣就實(shí)現(xiàn)了暗黑模式的適配了褥蚯。


一挚冤、定義的宏:


代碼中只要是設(shè)置顏色的地方就用定義好的顏色。(下面?zhèn)€別宏只是我的項(xiàng)目場(chǎng)景中會(huì)使用到的赞庶,并不適用于所有APP训挡,可自行針對(duì)自己的項(xiàng)目定義。有些顏色兩種模式下沒有變動(dòng))

/// 暗黑模式 YES是

#define CKDarkMode @available(iOS 13.0, *) && UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark

// MARK: - 十六進(jìn)制顏色

#define HexOf(rgbValue) Hex_A(rgbValue,1.0)

#define Hex_A(rgbValue,a) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:a]

// MARK: - 用全局變量設(shè)置背景歧强、文字,可以優(yōu)雅的主題切換 (取全局唯一性的名稱,便于維護(hù);最前面的優(yōu)先級(jí)最高)

#define Color_Bg? ? ? ? CKDarkMode?HexOf(0x191C32):HexOf(0xf4f4f4) //背景主題顏色? ? 黑色/白色

#define Color_ContView? CKDarkMode?HexOf(0x1D213B):HexOf(0xffffff) //內(nèi)容澜薄、cell顏色? 深藍(lán)色/白色 如果背景和cell顏色一樣,就都用這個(gè)

#define Color_Title? ? CKDarkMode?HexOf(0xFFFFFF):HexOf(0x393939) //主文字顏色? ? ? 白色/黑色

#define Color_Subtitle? CKDarkMode?HexOf(0x999999):HexOf(0x999999) //副文字顏色? ? ? 淺白色/灰色

#define Color_Green? ? CKDarkMode?HexOf(0x45C98F):HexOf(0x45C98F) //綠漲? ? ? ? ? (行情、交易)

#define Color_Red? ? ? CKDarkMode?HexOf(0xEF0C47):HexOf(0xEF0C47) //紅跌? ? ? ? ? (行情摊册、交易)

//

#define Color_NavBg? ? CKDarkMode?HexOf(0x1D213B):HexOf(0xffffff) //導(dǎo)航欄背景顏色

#define Color_NavTitle? CKDarkMode?HexOf(0xFFFFFF):HexOf(0x393939) //導(dǎo)航欄標(biāo)題顏色

#define Color_TabbarBg? CKDarkMode?HexOf(0x1D213B):HexOf(0xffffff) //標(biāo)簽欄背景顏色

#define Color_Selected? CKDarkMode?HexOf(0x46CA8F):HexOf(0x46CA8F) //綠色 (按鈕選中肤京、已認(rèn)證狀態(tài)的顏色)

#define Color_Line? ? ? CKDarkMode?HexOf(0x191C32):HexOf(0xf4f4f4) //分割線

#define Color_DarkGray? CKDarkMode?HexOf(0x333333):HexOf(0x333333) //深灰色

#define Color_Gray? ? ? CKDarkMode?HexOf(0x666666):HexOf(0x666666) //灰色

#define Color_LightGray CKDarkMode?HexOf(0x999999):HexOf(0x999999) //淺灰色

#define Color_InputBg? CKDarkMode?HexOf(0x191C32):HexOf(0xf4f4f4) //輸入框背景顏色

#define Color_DarkBlue? CKDarkMode?HexOf(0x191C32):HexOf(0x191C32) //深藍(lán)色 (特殊顏色)

#define Color_HalfTitle CKDarkMode?Hex_A(0x999999, 0.5):Hex_A(0x999999, 0.5);//半透明文字 色值是副標(biāo)題的一半

如果想關(guān)閉暗黑模式,直接設(shè)置:

#define CKDarkMode NO


二茅特、遇到的問題:


1忘分、最開始我是用的self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark去做判斷,但是有些類并沒有UITraitCollection這個(gè)屬性白修,很多地方報(bào)錯(cuò)妒峦。

解決方案:

改用UITraitCollection.current屬性來獲取當(dāng)前App的顏色模式。

2熬荆、CGColorRef相關(guān):

bt.layer.borderColor = Color_Selected.CGColor;

報(bào)錯(cuò):

Incompatible operand types ('UIColor * _Nonnull' and 'CGColorRef _Nonnull' (aka 'struct CGColor *'))

解決方案:

UIColor *color = Color_Selected;

bt.layer.borderColor = color.CGColor;

3.每次打開APP都能展示正常的模式舟山;但是如果打開APP后再切換模式,已經(jīng)加載出來的頁面依然會(huì)顯示切換之前的主題模式卤恳。

解決方案:

在頁面中添加通知累盗,獲取到切換主題的通知后重新刷新一下頁面顏色(類似于項(xiàng)目中的國(guó)際化通知處理邏輯)

4.項(xiàng)目中個(gè)別頁面的狀態(tài)欄是固定的白色,在切換頁面的時(shí)候會(huì)把狀態(tài)欄切換回主題顏色黑色突琳,但是在暗黑模式下就會(huì)有問題若债,因?yàn)榘岛谀J较抡麄€(gè)APP的狀態(tài)欄都是白色的,這時(shí)不需要切換回黑色拆融。

解決方案:

添加一個(gè)UIStatusBarStyle變量記錄主題狀態(tài)欄顏色蠢琳,這樣可以不用在控制器內(nèi)做太多額外的判斷。如果用 @available(iOS 13.0, *) 去做判斷镜豹,需求變更后還要每個(gè)地方都去改動(dòng)代碼傲须。用了這種方式,后面即使更改了主題或者關(guān)閉了暗黑模式趟脂,也不用一一去改代碼泰讽;也可以通過上面定義的宏CKDarkMode做判斷,關(guān)閉暗黑模式時(shí)只需把CKDarkMode設(shè)置為NO就行

UIStatusBarStyle _themeStatusBarStyle;//記錄主題狀態(tài)欄顏色

- (void)viewWillAppear:(BOOL)animated {

? ? [super viewWillAppear:animated];

? ? _themeStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;

? ? // 設(shè)置狀態(tài)欄顏色為白色

? ? [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;

}

- (void)viewWillDisappear:(BOOL)animated{

? ? [super viewWillDisappear:animated];

? ? // 恢復(fù)狀態(tài)欄顏色為主題顏色

? ? [UIApplication sharedApplication].statusBarStyle = _themeStatusBarStyle;

}

5、使用了宏的地方都會(huì)報(bào)警告已卸,提示我要做系統(tǒng)版本判斷佛玄,但是實(shí)際上我已經(jīng)在CKDarkMode中判斷過了,系統(tǒng)檢測(cè)不到:

'currentTraitCollection' is only available on iOS 13.0 or newer

解決方案:使用UIColor擴(kuò)展累澡。

999+的警告有點(diǎn)影響代碼視覺體驗(yàn)梦抢,后面應(yīng)該會(huì)改用擴(kuò)展的方式。如果有更好的解決方案請(qǐng)?jiān)谙路搅粞浴?/p>


三愧哟、UITraitCollection介紹:


1奥吩、在 iOS 13 中,我們可以通過 UITraitCollection 來判斷當(dāng)前系統(tǒng)的模式翅雏。UIView 和 UIViewController 圈驼、UIScreen、UIWindow都已經(jīng)遵從了UITraitEnvironment這個(gè)協(xié)議望几,因此這些類都擁有一個(gè)叫做?traitCollection的屬性绩脆,在這些類中,我們可以這樣去判斷當(dāng)前 App 的顏色模式:

BOOL isDark = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark);

2橄抹、另外靴迫,我們還可以通過?UITraitCollection.current這個(gè)屬性來獲取當(dāng)前 App 的顏色模式。

3楼誓、如果暫時(shí)不想開放這個(gè)功能玉锌,可以先暫時(shí)全局關(guān)閉暗黑模式:

在 Info.plist 文件中,添加 key 為 User Interface Style疟羹,類型為 String主守,value 設(shè)置為?Light (Dark)即可,如果重新打開就把這條設(shè)置刪除榄融。(這種方式是在APP整個(gè)生命周期內(nèi)關(guān)閉了暗黑模式参淫;上面的設(shè)置#define CKDarkMode NO只是用代碼做了判斷并只在用了宏的地方起作用)。

4愧杯、在 iOS 13中涎才,UIView、UIViewController 力九、UIWindow有了一個(gè)?overrideUserInterfaceStyle的新屬性耍铜,可以覆蓋系統(tǒng)的模式。

單個(gè)頁面或視圖關(guān)閉暗黑模式跌前,設(shè)置?overrideUserInterfaceStyle為對(duì)應(yīng)的模式棕兼,強(qiáng)制限制該視圖與其子視圖以設(shè)置的模式進(jìn)行展示,不跟隨系統(tǒng)模式改變進(jìn)行改變抵乓。

self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;

設(shè)置此屬性會(huì)影響當(dāng)前view / viewController / window以及它下面的任何內(nèi)容程储。

如果你希望一個(gè)子視圖監(jiān)聽系統(tǒng)的模式蹭沛,請(qǐng)將?overrideUserInterfaceStyle屬性設(shè)置為.unspecified臂寝。


四章鲤、拓展


除了我的這種實(shí)現(xiàn)方案,還有其他方案可以適配暗黑模式:

1咆贬、UIColor擴(kuò)展:

+(UIColor *)generateDynamicColor:(UIColor *)lightColor darkColor:(UIColor *)darkColor{

????if(@available(iOS 13.0, *)) {

? ? ? ? UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {

????????????if(traitCollection.userInterfaceStyle == UIUserInterfaceStyleLight) {

????????????????return lightColor;

? ? ? ? ? ? }else{

????????????????return darkColor;

? ? ? ?}}];

????????return dyColor;

? ? }else{

????????return lightColor;

}}

問題:

這種寫法要在每個(gè)使用的地方分別傳一個(gè)普通模式的顏色和暗黑模式的顏色败徊,不方便維護(hù)。

解決方案:

可以定義幾個(gè)常用顏色函數(shù)掏缎,特殊的場(chǎng)景就用上面的方法皱蹦,這樣就不需要在每個(gè)地方都控制顏色值了。

+(UIColor *)ContViewColor{

????if(@available(iOS 13.0, *)) {

? ? ? ? UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {

????????????if(traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {

????????????????return ColorA;? ? ? ? ? ?

????????????}else{

????????????????return ColorB; ? ? ? ? ??

???????}?}];

????????return dyColor;? ?

????}else{

????????return ColorB; ??

}}

2眷蜈、可以在?Images.xcassets中定義幾種常用顏色沪哺,并為顏色再配置一個(gè)用于暗黑模式的對(duì)應(yīng)的顏色:

3、在?Images.xcassets中配置不同模式下的圖片酌儒,當(dāng)你設(shè)置為暗黑模式后就會(huì)自動(dòng)顯示對(duì)應(yīng)的圖片

4辜妓、啟動(dòng)圖:

LaunchScreen.storyboard可以像普通的圖片那樣針對(duì)深色模式設(shè)置另外的一張圖片

5、layer:

UIColor *resolvedColor = [[UIColor labelColor] resolvedColorWithTraitCollection:self.view.traitCollection];

label.layer.borderColor = resolvedColor.CGColor;

6忌怎、UIActivityIndicatorView 的 style:

iOS 13前 的 UIActivityIndicatorViewStyle:

typedef NS_ENUM(NSInteger, UIActivityIndicatorViewStyle) {?

? ? UIActivityIndicatorViewStyleWhiteLarge,

? ? UIActivityIndicatorViewStyleWhite,

? ? UIActivityIndicatorViewStyleGray,

};

iOS 13后籍滴,由于暗黑模式,上述三個(gè)屬性都被廢棄榴啸,建議使用如下 style:

typedef NS_ENUM(NSInteger, UIActivityIndicatorViewStyle) {

????UIActivityIndicatorViewStyleMedium,

????UIActivityIndicatorViewStyleLarge,

????UIActivityIndicatorViewStyleWhiteLarge API_DEPRECATED_WITH_REPLACEMENT("UIActivityIndicatorViewStyleLarge",

????UIActivityIndicatorViewStyleWhite API_DEPRECATED_WITH_REPLACEMENT("UIActivityIndicatorViewStyleMedium",

????UIActivityIndicatorViewStyleGray API_DEPRECATED_WITH_REPLACEMENT("UIActivityIndicatorViewStyleMedium",

};

7孽惰、Status Bar 的 style :

在 iOS 13 之前,狀態(tài)欄的樣式的枚舉值也帶有著明顯的顏色傾向鸥印,UIStatusBarStyleDefault勋功、UIStatusBarStyleLightContent。

現(xiàn)在库说,狀態(tài)欄的 default 樣式會(huì)根據(jù)當(dāng)前的模式展示不同的顏色狂鞋,而原有的?lightContent樣式則新增一個(gè)?darkContent的樣式與之對(duì)應(yīng)。

typedef NS_ENUM(NSInteger, UIStatusBarStyle) {

? ? UIStatusBarStyleDefault? ? ? = 0, // Automatically chooses light or dark content based on the user interface style

? ? UIStatusBarStyleLightContent = 1, // Light content,foruse on dark backgrounds? ?

????UIStatusBarStyleDarkContent? = 3, // Dark content,foruse on light backgrounds

};

8璃弄、SF Symbols


注意:

命名時(shí)要保證這個(gè)名字的全局唯一性要销,避免和項(xiàng)目中其他命名雷同,這樣可以保證全局搜索時(shí)搜索到的結(jié)果只有你想搜索的內(nèi)容夏块,便于維護(hù)疏咐。例如你取名RedColor,會(huì)搜索到很多其他沒用的信息脐供。這種命名思路也可以用在其他地方浑塞。

除了改背景顏色、文字顏色政己,還需要替換圖標(biāo)酌壕、圖片,這個(gè)需要UI配合切圖。

參考:

How To Adopt Dark Mode In Your iOS App

DarkMode1

DarkMode2

DarkMode3

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卵牍,一起剝皮案震驚了整個(gè)濱河市果港,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糊昙,老刑警劉巖辛掠,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異释牺,居然都是意外死亡萝衩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門没咙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猩谊,“玉大人,你說我怎么就攤上這事祭刚∨平荩” “怎么了?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵袁梗,是天一觀的道長(zhǎng)宜鸯。 經(jīng)常有香客問我,道長(zhǎng)遮怜,這世上最難降的妖魔是什么淋袖? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮锯梁,結(jié)果婚禮上即碗,老公的妹妹穿的比我還像新娘。我一直安慰自己陌凳,他們只是感情好剥懒,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著合敦,像睡著了一般初橘。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上充岛,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天保檐,我揣著相機(jī)與錄音,去河邊找鬼崔梗。 笑死夜只,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蒜魄。 我是一名探鬼主播扔亥,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼场躯,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了旅挤?” 一聲冷哼從身側(cè)響起踢关,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谦铃,沒想到半個(gè)月后耘成,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡驹闰,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撒会。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘹朗。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖诵肛,靈堂內(nèi)的尸體忽然破棺而出屹培,到底是詐尸還是另有隱情,我是刑警寧澤怔檩,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布褪秀,位于F島的核電站,受9級(jí)特大地震影響薛训,放射性物質(zhì)發(fā)生泄漏媒吗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一乙埃、第九天 我趴在偏房一處隱蔽的房頂上張望闸英。 院中可真熱鬧,春花似錦介袜、人聲如沸甫何。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辙喂。三九已至,卻和暖如春鸠珠,著一層夾襖步出監(jiān)牢的瞬間巍耗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工跳芳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芍锦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓飞盆,卻偏偏與公主長(zhǎng)得像娄琉,于是被迫代替她去往敵國(guó)和親次乓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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

  • 今天的 WWDC 19 上發(fā)布了 iOS 13孽水,我們來看下如何適配 DarkMode 首先我們來看下效果圖 如何適...
    RayJiang97閱讀 48,328評(píng)論 28 92
  • iOS 13終于引來了暗黑模式票腰。 每當(dāng)新特性的到來,iOS開發(fā)者們既緊張又有點(diǎn)小興奮女气,懷揣著被虐的心態(tài)杏慰,讓我們來看...
    koin447閱讀 63,681評(píng)論 16 106
  • 1.NSTimer //暫停if ([timer isValid]) {[timer setFireDate:[N...
    俊月閱讀 1,342評(píng)論 0 0
  • 一些特殊部位,比如胳膊這類血管豐富的地方炼鞠,暴露等級(jí)需要酌情升級(jí)缘滥。 一般第一次去接種,醫(yī)生都會(huì)建議打免疫球蛋白谒主,其目...
    JasmineZHU閱讀 613評(píng)論 0 1
  • 前兩天在網(wǎng)絡(luò)上看到一段話:“這世界上有一種父母霎肯,從不正兒八經(jīng)的跟你講道理擎颖,也不會(huì)煩人的碎碎念。童心未泯的他們玩起來...
    到處亂撞的魚閱讀 1,423評(píng)論 1 7