筆者最近看了DarkMode相關的內容。本文將會講解亮/暗模式切換時statusBar(狀態欄)、UIActivityIndicatorView(活動指示器視圖)、文字繪制顏色及設置layer,border顏色、UIVisualEffectView適配DarkMode的相關內容。
StatusBar
打算做DarkMode適配的開發者需要注意使用的statusBar的類型。
使用UIStatusBarStyleLightContent類型statusBar的開發者需要注意Light模式下,狀態欄部分的內容是無法看清的。
接下來想使用UIStatusBarStyleDarkContent類型statusBar的開發者需要注意Dark模式下,狀態欄部分的內容是無法看清的。
除了我們想要App或某些界面只能支持DarkMode或者支持LightMode的情況,我們要使用UIStatusBarStyleDefault類型的statusBar。
不同類型的狀態欄在亮、暗模式下的相關示意圖如下:
示例代碼
- (UIStatusBarStyle)preferredStatusBarStyle {
return UIStatusBarStyleDefault;
}
更多狀態欄顯示樣式的內容,大家可以查看dac_1033的關于iOS 狀態欄、導航欄的幾處筆記
UIActivityView
使用UIActivityIndicatorView的UIActivityIndicatorViewStyleWhiteLarge的開發者需要注意,UIActivityIndicatorViewStyleWhiteLarge樣式的UIActivityIndicatorView無法在DarkMode模式下基本看不見。其中MBProgressHUD使用過UIActivityIndicatorView,不過因為MBProgressHUD在外側嵌套了一層黑色容器視圖,所以在Dark模式下運行效果尚可。
推薦開發者使用UIActivityIndicatorViewStyleLarge或UIActivityIndicatorViewStyleMedium類型的UIActivityIndicatorView。
相關示意圖如下,可看出只有中間的UIActivityIndicatorViewStyleLarge類型的UIActivityIndicatorView可以再亮、暗模式下均能正常使用,當然開發者也可以選擇去改變UIActivityIndicatorView的tintColor來適配DarkMode。
示例代碼
UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
繪制文字顏色和CGColor
繪制文字顏色
如果我們使用drawRect繪制屬性字符串時,沒有指定顏色,系統會默認為文字顏色為黑色。或者之前我們設置的是較暗的一個顏色。相關情況在Dark模式下,相應文字基本會看不清楚。
示意圖如下:
那么為了繪制的文字在切換亮/暗模式的時候可以正常顯示,我們需要指定繪制的文字的顏色。比如明確設置
textAttris[NSForegroundColorAttributeName] = [UIColor labelColor];
注意字符串屬性字典nil Value
因為指定字符串屬性字典的方式不同,當創建字符串的屬性字典的時候,大家可能會遇到如下的一個問題。
exception:*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[0]
上述錯誤是往字典中插入nil的一種異常提示。即此時,我們要注意[UIColor colorNamed:@"colorName"] 的值不為空。
if (@available(iOS 13.0, *)) {
UIColor *forgroundColor = [UIColor colorNamed:@"colorName"];
if (forgroundColor) {
NSAttributedString *attriString = [[NSAttributedString alloc] initWithString:@"QiShare" attributes:@{NSForegroundColorAttributeName: forgroundColor}];
NSLog(@"屬性字符串%@", attriString);
}
}
CGColor
使用到CGColor的部分,如設置Layer顏色,border顏色。設置Layer顏色會發現在亮、暗模式切換的時候,不能順利地得以切換,可以在適當的時機進行顏色設置,筆者認為可以在traitCollectionDidChange的時候多設置一次相應的顏色以避免某些切換不流暢現象。
相關示意圖如下。
截圖中的2個視圖,上邊的視圖不能隨著亮/暗模式做相應切換。下邊的視圖可以隨著亮/暗模式做相應切換。
相關實現可以在- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
中操作。
示例代碼:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
self.normalColorLayer.backgroundColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
return [UIColor redColor];
} else {
return [UIColor blueColor];
}
}].CGColor;
self.normalColorLayer.borderColor = [UIColor systemBackgroundColor].CGColor;
}
}
UIVisualEffectView
關于UIVisualEffectView,筆者了解得比較少,Xs·H 也提到相關場景還有應用退到后臺的時候的模糊遮罩。根據官方Demo和文檔做了如下效果的圖,大家有需要可以下載QiDarkMode查看相關代碼實現。
官方文檔相關描述:
Visual-effect views add transparency to your background views, which gives your UI more visual depth than if the backgrounds were opaque. To ensure that your content remains visible, visual-effect views blur the background content subtly and add vibrancy effects to adjust the colors of your foreground content automatically. The system updates these effects dynamically, ensuring that your app's content remains visible when the underlying content changes.
示例代碼:
// 創建UIVisualEffectView
UIBlurEffect *blurEffect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemThinMaterial];
UIVisualEffectView *visualEffectView = [[UIVisualEffectView alloc] initWithEffect:blurEffect];
visualEffectView.frame = CGRectMake(.0, 100.0, CGRectGetWidth(self.view.frame), 150.0);
[self.view addSubview:visualEffectView];
// 創建UIVibrancyEffect
UIVibrancyEffect *vibrancyEffect =
[UIVibrancyEffect effectForBlurEffect:blurEffect style:UIVibrancyEffectStyleSecondaryLabel];
UIVisualEffectView *vibrancyView = [[UIVisualEffectView alloc] initWithEffect:vibrancyEffect];
[visualEffectView.contentView addSubview:vibrancyView];
vibrancyView.frame = visualEffectView.bounds;
vibrancyView.frame = CGRectMake(.0, .0, CGRectGetWidth(visualEffectView.frame), CGRectGetHeight(visualEffectView.frame) * 0.5);
UILabel *label = [UILabel new];
label.font = [UIFont preferredFontForTextStyle:UIFontTextStyleLargeTitle compatibleWithTraitCollection:self.traitCollection];
label.text = @"Vibrant Label1";
[vibrancyView.contentView addSubview:label];
label.frame = vibrancyView.bounds;
其他
固定設置App或某些界面亮/暗模式
- 固定設置App亮/暗模式
固定亮模式:設置info.plist文件中的User Interface Style為"Light"
固定暗模式:設置info.plist文件中的User Interface Style為"Dark"
- 固定某些頁面為亮/暗模式
方式:相應控制器重寫overrideUserInterfaceStyle。
固定亮模式:
- (UIUserInterfaceStyle)overrideUserInterfaceStyle {
return UIUserInterfaceStyleLight;
}
固定暗模式:
- (UIUserInterfaceStyle)overrideUserInterfaceStyle {
return UIUserInterfaceStyleDark;
}
Demo
詳情見Demo:QiDarkMode
參考學習網址
- https://developer.apple.com/documentation/xcode/supporting_dark_mode_in_your_interface?language=objc
- https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW44
- https://webkit.org/blog/8840/dark-mode-support-in-webkit/
- https://www.fivestars.blog/code/ios-dark-mode-how-to.html
推薦文章:
2019蘋果秋季新品發布會速覽
申請蘋果開發者賬號的流程
Swift 5.1 (3) - 字符串
用Flutter 寫一個簡單頁面
5分鐘,帶你迅速上手Markdown語法
Swift 5.1 (2) - 運算符
Swift 5.1(1) - 基礎
Sign In With Apple(一)