最近公司業務需求要更換APP主題。最開始是一個地方一個地方去改,並且項目中不少老代碼是用xib寫的,習慣純代碼編程的我改的很難受。並且之後指不定要再次更改主題。html
因而我定義了幾個主要顏色的宏,代碼中只要是設置顏色的地方就用宏。這樣只須要改一次,當要切換主題的時候直接對宏進行更改就好了。ios
結合已作好的切換主題功能,再加上一個暗黑模式判斷,若是當前是暗黑模式就用A套色值,若是不是就用B套色值,這樣就實現了暗黑模式的適配了。編程
.bash
代碼中只要是設置顏色的地方就用定義好的顏色。(下面個別宏只是個人項目場景中會使用到的,並不適用於全部APP,可自行鍼對本身的項目定義。有些顏色兩種模式下沒有變更)app
/// 暗黑模式 YES是
#define CKDarkMode @available(iOS 13.0, *) && UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark
// MARK: - 十六進制顏色
#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: - 用全局變量設置背景、文字,能夠優雅的主題切換 (取全局惟一性的名稱,便於維護;最前面的優先級最高)
#define Color_Bg CKDarkMode?HexOf(0x191C32):HexOf(0xf4f4f4) //背景主題顏色 黑色/白色
#define Color_ContView CKDarkMode?HexOf(0x1D213B):HexOf(0xffffff) //內容、cell顏色 深藍色/白色 若是背景和cell顏色同樣,就都用這個
#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) //導航欄背景顏色
#define Color_NavTitle CKDarkMode?HexOf(0xFFFFFF):HexOf(0x393939) //導航欄標題顏色
#define Color_TabbarBg CKDarkMode?HexOf(0x1D213B):HexOf(0xffffff) //標籤欄背景顏色
#define Color_Selected CKDarkMode?HexOf(0x46CA8F):HexOf(0x46CA8F) //綠色 (按鈕選中、已認證狀態的顏色)
#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) //深藍色 (特殊顏色)
#define Color_HalfTitle CKDarkMode?Hex_A(0x999999, 0.5):Hex_A(0x999999, 0.5);//半透明文字 色值是副標題的一半
複製代碼
若是想關閉暗黑模式,直接設置:ide
#define CKDarkMode NO函數
.post
self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark
去作判斷,可是有些類並無UITraitCollection這個屬性,不少地方報錯。解決方案:ui
改用UITraitCollection.current屬性來獲取當前App的顏色模式。spa
bt.layer.borderColor = Color_Selected.CGColor;
複製代碼
報錯:
Incompatible operand types ('UIColor * _Nonnull' and 'CGColorRef _Nonnull' (aka 'struct CGColor *'))
解決方案:
UIColor *color = Color_Selected;
bt.layer.borderColor = color.CGColor;
複製代碼
解決方案:
在頁面中添加通知,獲取到切換主題的通知後從新刷新一下頁面顏色(相似於項目中的國際化通知處理邏輯)
解決方案:
添加一個UIStatusBarStyle變量記錄主題狀態欄顏色,這樣能夠不用在控制器內作太多額外的判斷。若是用 @available(iOS 13.0, *) 去作判斷,需求變動後還要每一個地方都去改動代碼。用了這種方式,後面即便更改了主題或者關閉了暗黑模式,也不用一一去改代碼;也能夠經過上面定義的宏CKDarkMode作判斷,關閉暗黑模式時只需把CKDarkMode設置爲NO就行
UIStatusBarStyle _themeStatusBarStyle;//記錄主題狀態欄顏色
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
_themeStatusBarStyle = [UIApplication sharedApplication].statusBarStyle;
// 設置狀態欄顏色爲白色
[UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent;
}
- (void)viewWillDisappear:(BOOL)animated{
[super viewWillDisappear:animated];
// 恢復狀態欄顏色爲主題顏色
[UIApplication sharedApplication].statusBarStyle = _themeStatusBarStyle;
}
複製代碼
'currentTraitCollection' is only available on iOS 13.0 or newer
解決方案:使用UIColor擴展。
999+的警告有點影響代碼視覺體驗,後面應該會改用擴展的方式。若是有更好的解決方案請在下方留言。
.
UIView 和 UIViewController 、UIScreen、UIWindow
都已經聽從了UITraitEnvironment
這個協議,所以這些類都擁有一個叫作 traitCollection
的屬性,在這些類中,咱們能夠這樣去判斷當前 App 的顏色模式:BOOL isDark = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark);
UITraitCollection.current
這個屬性來獲取當前 App 的顏色模式。在 Info.plist 文件中,添加 key 爲 User Interface Style,類型爲 String,value 設置爲 Light (Dark)
便可,若是從新打開就把這條設置刪除。(這種方式是在APP整個生命週期內關閉了暗黑模式;上面的設置#define CKDarkMode NO
只是用代碼作了判斷並只在用了宏的地方起做用)。
UIView、UIViewController 、UIWindow
有了一個 overrideUserInterfaceStyle
的新屬性,能夠覆蓋系統的模式。單個頁面或視圖關閉暗黑模式,設置 overrideUserInterfaceStyle
爲對應的模式,強制限制該視圖與其子視圖以設置的模式進行展現,不跟隨系統模式改變進行改變。
self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;
設置此屬性會影響當前view / viewController / window
以及它下面的任何內容。
若是你但願一個子視圖監聽系統的模式,請將 overrideUserInterfaceStyle
屬性設置爲.unspecified
。
.
除了個人這種實現方案,還有其餘方案能夠適配暗黑模式:
+(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;
}
}
複製代碼
問題:
這種寫法要在每一個使用的地方分別傳一個普通模式的顏色和暗黑模式的顏色,不方便維護。
解決方案:
能夠定義幾個經常使用顏色函數,特殊的場景就用上面的方法,這樣就不須要在每一個地方都控制顏色值了。
+(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;
}
}
複製代碼
Images.xcassets
中定義幾種經常使用顏色,併爲顏色再配置一個用於暗黑模式的對應的顏色:Images.xcassets
中配置不一樣模式下的圖片,當你設置爲暗黑模式後就會自動顯示對應的圖片LaunchScreen.storyboard
能夠像普通的圖片那樣針對深色模式設置另外的一張圖片
UIColor *resolvedColor = [[UIColor labelColor] resolvedColorWithTraitCollection:self.view.traitCollection];
label.layer.borderColor = resolvedColor.CGColor;
複製代碼
iOS 13前 的 UIActivityIndicatorViewStyle:
typedef NS_ENUM(NSInteger, UIActivityIndicatorViewStyle) {
UIActivityIndicatorViewStyleWhiteLarge,
UIActivityIndicatorViewStyleWhite,
UIActivityIndicatorViewStyleGray,
};
複製代碼
iOS 13後,因爲暗黑模式,上述三個屬性都被廢棄,建議使用以下 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",
};
複製代碼
在 iOS 13 以前,狀態欄的樣式的枚舉值也帶有着明顯的顏色傾向,UIStatusBarStyleDefault、UIStatusBarStyleLightContent
。
如今,狀態欄的 default 樣式會根據當前的模式展現不一樣的顏色,而原有的 lightContent
樣式則新增一個 darkContent
的樣式與之對應。
typedef NS_ENUM(NSInteger, UIStatusBarStyle) {
UIStatusBarStyleDefault = 0, // Automatically chooses light or dark content based on the user interface style
UIStatusBarStyleLightContent = 1, // Light content, for use on dark backgrounds
UIStatusBarStyleDarkContent = 3, // Dark content, for use on light backgrounds
};
複製代碼
.
命名時要保證這個名字的全局惟一性,避免和項目中其餘命名雷同,這樣能夠保證全局搜索時搜索到的結果只有你想搜索的內容,便於維護。例如你取名RedColor,會搜索到不少其餘沒用的信息。這種命名思路也能夠用在其餘地方。
除了改背景顏色、文字顏色,還須要替換圖標、圖片,這個須要UI配合切圖。
How To Adopt Dark Mode In Your iOS App
DarkMode1
DarkMode2
DarkMode3