iOS13 DarkMode适配(二)

avatar
奇舞团移动端团队 @奇舞团

级别: ★☆☆☆☆
标签:「iOS 13」「Dark Mode」
作者: WYW
审校: QiShare团队


笔者最近看了DarkMode相关的内容。本文将会讲解亮/暗模式切换时statusBar(状态栏)、UIActivityIndicatorView(活动指示器视图)、文字绘制颜色及设置layer,border颜色、UIVisualEffectView。

StatusBar

打算做DarkMode适配的开发者需要注意使用的statusBar的类型。

  • 使用UIStatusBarStyleLightContent类型statusBar的开发者需要注意Light模式下,状态栏部分的内容是无法看清的。

  • 接下来想使用UIStatusBarStyleDarkContent类型statusBar的开发者需要注意Dark模式下,状态栏部分的内容是无法看清的。

  • 除了我们想要App或某些界面只能支持DarkMode或者支持LightMode的情况,我们要使用UIStatusBarStyleDefault类型的statusBar。

不同类型的状态栏在亮、暗模式下的相关示意图如下:

qiLightStatusBar1.png

qiLightStatusBar2.png

qiLightStatusBar3.png

qiDarkStatusBar11.png

qiDarkStatusBar22.png

qiDarkStatusBar33.png

示例代码

- (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。

QiActivityView1.png

QiActivityView2.png

QiActivityView3.png

示例代码

    UIActivityIndicatorView *indicatorView = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];

绘制文字颜色和CGColor

绘制文字颜色

如果我们使用drawRect绘制属性字符串时,没有指定颜色,系统会默认为文字颜色为黑色。或者之前我们设置的是较暗的一个颜色。相关情况在Dark模式下,相应文字基本会看不清楚。

示意图如下:

QiDrawTextColorUnNormal.png

QiDrawTextColorNormal.png

那么为了绘制的文字在切换亮/暗模式的时候可以正常显示,我们需要指定绘制的文字的颜色。比如明确设置

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的时候多设置一次相应的颜色以避免某些切换不流畅现象。

相关示意图如下。

QiLayerBorderColor1.png

QiLayerBorderColor2.png

截图中的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.

QiVisualEffectView.png

示例代码:

   // 创建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

参考学习网址


推荐文章:
2019苹果秋季新品发布会速览
申请苹果开发者账号的流程
Swift 5.1 (3) - 字符串
用Flutter 写一个简单页面
5分钟,带你迅速上手Markdown语法
Swift 5.1 (2) - 运算符
Swift 5.1(1) - 基础
Sign In With Apple(一)
奇舞周刊