一夸盟、源起
蘋果公司要求酱塔,在5月之前必須完成對于iOS13新增的暗黑模式適配。來源于http://www.cocoachina.com/articles/896909?filter=rec弟蚀。這里趁有時間拌消,找了點(diǎn)資料挑豌。大概屢了一下邏輯。不過按照蘋果的尿性。估計(jì)不會這么快就要求氓英。
二侯勉、適配原理
將同一個資源,創(chuàng)建出兩種模式的樣式铝阐。系統(tǒng)根據(jù)當(dāng)前選擇的樣式址貌,自動獲取該樣式的資源。
每次系統(tǒng)更新樣式時徘键,應(yīng)用會調(diào)用當(dāng)前所有存在的元素調(diào)用對應(yīng)的一些重新方法练对,進(jìn)行重繪視圖,可以在對應(yīng)的方法做相應(yīng)的改動啊鸭。根據(jù)系統(tǒng)新增的一些通知方法和對于一些主要顯示類新增的刷新方法 做一些判斷處理锹淌。
三、圖片適配
1.創(chuàng)建一個Assets文件(或在現(xiàn)有的Assets文件中)
2.新建一個圖片資源文件(或者顏色資源文件赠制、或者其他資源文件)
3.選中該資源文件赂摆, 打開 Xcode ->View ->Inspectors ->Show Attributes Inspectors (或者Option+Command+4)視圖,將 Apperances 選項(xiàng) 改為Any钟些,Dark
4.執(zhí)行完第三步烟号,資源文件將會有多個容器框,分別為 Any Apperance 和 Dark Apperance. Any Apperance 應(yīng)用于默認(rèn)情況(Unspecified)與高亮情況(Light)政恍, Dark Apperance 應(yīng)用于暗黑模式(Dark)
代碼默認(rèn)執(zhí)行時汪拥,就可以正常通過名字使用了,系統(tǒng)會根據(jù)當(dāng)前模式自動獲取對應(yīng)的資源文件篙耗。
四迫筑、顏色適配
iOS13之前的UIColor 只能返回一種特定的顏色,iOS13及其以后可以返回動態(tài)的顏色
UIColor增加了兩個初始化方法宗弯,使用以下方法可以創(chuàng)建動態(tài)UIColor
注:一個是類方法脯燃,一個是實(shí)例方法,兩個方法如下:
+ (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *traitCollection))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
- (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *traitCollection))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
方法需要傳一個block
當(dāng)系統(tǒng)在LightModel 跟DarkMode中進(jìn)行切換的時候蒙保,會立刻回調(diào)這個方法(應(yīng)用不管處于前臺還是后臺)
block中返回當(dāng)前的traitCollection對象辕棚,我們根據(jù)它的屬性userInterfaceStyle 判斷當(dāng)前的顯示模式,block中返回對應(yīng)模式下的color
UIUserInterfaceStyle 枚舉類型如下:
typedef NS_ENUM(NSInteger, UIUserInterfaceStyle) {
UIUserInterfaceStyleUnspecified,
UIUserInterfaceStyleLight,
UIUserInterfaceStyleDark,
}
示例代碼??
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
NSLog(@"traitCollectionDidChange");
//創(chuàng)建動態(tài) color
UIColor *color = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor darkGrayColor];
} else {
return [UIColor redColor];
}
}];
self.view.backgroundColor = color;
}
五、繪圖適配
iOS13后邓厕,UIColor能夠表示動態(tài)顏色逝嚎,但是CGColor依然只能表示一種顏色,那么對于CALayer等對象如何適配暗黑模式呢?
有幾下三種方式進(jìn)行表示
方法如下:
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
NSLog(@"traitCollectionDidChange");
if (@available(iOS 13.0, *)) {
//創(chuàng)建動態(tài) bgColor
UIColor *bgColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor blackColor];
} else {
return [UIColor whiteColor];
}
}];
self.view.backgroundColor = bgColor;
//創(chuàng)建動態(tài) layerColor
UIColor *layerColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor darkGrayColor];
} else {
return [UIColor redColor];
}
}];
//改變layer 顏色
//1.方法一
// UIColor *resolveColor = [layerColor resolvedColorWithTraitCollection:self.traitCollection];
// layer.backgroundColor = resolveColor.CGColor;
//2.方法二
// [self.traitCollection performAsCurrentTraitCollection:^{
// UIColor *resolveColor = [layerColor resolvedColorWithTraitCollection:self.traitCollection];
// layer.backgroundColor = resolveColor.CGColor;
// }];
//3.方法三
layer.backgroundColor = layerColor.CGColor;
} else {
NSLog(@"不是iOS13 所以不需要暗黑模式");
}
}
六详恼、刷新掉用
當(dāng)用戶更改外觀時补君,系統(tǒng)會通知所有window與View需要更新樣式,在此過程中iOS會觸發(fā)以下方法, 完整的觸發(fā)方法文檔
UIView:中可以調(diào)用如下方法進(jìn)行更改
traitCollectionDidChange(_:)
layoutSubviews()
draw(_:)
updateConstraints()
tintColorDidChange()
UIViewController:中可以調(diào)用如下方法進(jìn)行更改
traitCollectionDidChange(_:)
updateViewConstraints()
viewWillLayoutSubviews()
viewDidLayoutSubviews()
UIPresentationController:中可以調(diào)用如下方法進(jìn)行更改
traitCollectionDidChange(_:)
containerViewWillLayoutSubviews()
containerViewDidLayoutSubviews()
七昧互、幾種常見適配方式
1> 默認(rèn)情況下應(yīng)用不做任何處理是需要跟隨手機(jī)系統(tǒng)做暗黑適配
2> 如果整個應(yīng)用中堅(jiān)持是Light 或者Dark模式赚哗,需要在info.plist
中 添加 UIUserInterfaceStyle 為 Light 或者Dark
3> 部分UIView / UIViewcontroller / UIWindow 不跟隨手機(jī)暗黑適配
a.UIViewController與UIView 都新增一個屬性 -overrideUserInterfaceStyle
b.將 -overrideUserInterfaceStyle 設(shè)置為對應(yīng)的模式她紫,則強(qiáng)制限制該元素與其子元素以設(shè)置的模式進(jìn)行展示,不跟隨系統(tǒng)模式改變進(jìn)行改變
c.-overrideUserInterfaceStyle影響范圍屿储。
設(shè)置 ViewController 的該屬性, 將會影響視圖控制器的視圖和子視圖控制器采用該樣式渐逃,不影響彈出的vc
設(shè)置 View 的該屬性够掠, 將會影響視圖及其所有子視圖采用該樣式
設(shè)置 Window 的該屬性, 將會影響窗口中的所有內(nèi)容都采用樣式茄菊,包括根視圖控制器和在該窗口中顯示內(nèi)容的所有演示控制器(UIPresentationController)
/*
* When set on an ordinary `UIView`:
* - This property affects only the traits of this view and its subviews.
* - It does not affect any view controllers, or any subviews that are owned by different view controllers.
*
* When set on a `UIWindow`:
* - This property affects the `rootViewController` and thus the entire view controller and view hierarchy.
* - It also affects presentations that happen inside the window.
*/
@property (nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
八疯潭、自動輸出log
模式切換時自動打印log,就不需要我們一次又一次的執(zhí)行po命令了
在Xcode菜單欄Product->Scheme->Edit Scheme
選擇Run->Arguments->Arguments Passed On Launch
添加以下命令即可
-UITraitCollectionChangeLoggingEnabled YES
當(dāng)切換模式就會在控制臺打印出信息面殖,信息如下:
九竖哩、相關(guān)資料
demo: https://github.com/sisios/DarkModel.git
WWDC視頻:https://developer.apple.com/videos/play/wwdc2019/214/
http://www.reibang.com/p/7925bd51d2d6
http://www.reibang.com/p/e6616e44cf60
http://www.reibang.com/p/476cac3851c8
十、項(xiàng)目實(shí)戰(zhàn)
我們項(xiàng)目用的DKNightVersion三方適配夜間模式脊僚,為了更好的兼容iOS13暗黑模式相叁,我在第一個頁簽的viewcontroller的重寫*-(void)traitCollectionDidChange:(UITraitCollection )previousTraitCollection方法
當(dāng)app被殺死,修改暗黑模式后辽幌,重啟app
-(void)applicationDidFinishLaunching:(UIApplication *)application {
//暗黑模式
if (@available(iOS 13.0, *)) {
UIUserInterfaceStyle style = UITraitCollection.currentTraitCollection.userInterfaceStyle;
if (style == UIUserInterfaceStyleDark) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"showNightMode"];
[self.dk_manager nightFalling];
} else {
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"showNightMode"];
[self.dk_manager dawnComing];
}
} else {
NSLog(@"不是iOS13增淹,不需要暗黑模式");
}
}
當(dāng)app處于active(前臺或者后臺)
//暗黑模式
-(void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
NSLog(@"traitCollectionDidChange");
//創(chuàng)建動態(tài) color
if (@available(iOS 13.0, *)) {
UIColor *color = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"showNightMode"];
[self.dk_manager nightFalling];
return [UIColor blackColor];
} else {
[[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"showNightMode"];
[self.dk_manager dawnComing];
return [UIColor whiteColor];
}
}];
self.view.backgroundColor = color;
} else {
NSLog(@"不是iOS13版本不需要暗黑模式");
}
}
注意點(diǎn)
項(xiàng)目中全部使用了UIColor的地方一定要注意了。一些默認(rèn)的顏色 比如Cell背景色 UIView背景色 不要在設(shè)置白色 這樣他們顏色就跟隨系統(tǒng)的 顏色顯示 自動切換了乌企。