美文网首页
关于iOS13 DarkMode的适配那些事儿

关于iOS13 DarkMode的适配那些事儿

作者: 重庆妹子在霾都 | 来源:发表于2020-03-15 16:01 被阅读0次

一、为什么要调研这个暗黑模式的适配?

在2020年3月4日,苹果粑粑可能心情大好发了这么一个公告:


image.png

在这个公告里面,苹果提了三个要求:
1.至2020年4月30日起,开发者必须使用iOS1或更高版本打包才能提交到App Store
2.至2020年4月30日起,开发者必使用storyboard来提供应用的launchScreen
3.至2020年4月30日起,开发者需要给自己的应用完成所有屏幕尺寸的适配

公告中提到了Dark Mode,但是并没有明确表示需要在2020年4月30日之前完成Dark Mode的适配,所以关于网上那些说不适配Dark Mode会导致应用下架的完全是无稽之谈,那么可能很多童鞋又会问Apple是不是支持开发者做这个Dark Mode,答案是肯定的,苹果为了Dark Mode做了很多工作,比如说提供了一些接口、各种适配指南等,但是这种支持只是“强烈建议”还远远没有上升到不支持Dark Mode就要下架的程度。
个人认为,在遥远的未来,可能苹果粑粑真的会要求开发者要对自己的 应用做Dark Mode的适配,话不多说,我们接下来就来看看怎么来做这个暗黑模式(Dark Mode)的适配?

二、Dark Mode有哪些适配方案,各自的优缺点是什么?

对于广大iOS开发者来说,适配Dark Mode并不像设置语言那么简单,设置语言之后,手机会重启,即便是开发者自己实现的语言切换的功能 切换语言也会让App重新初始化,相反Dark Mode是要求在切换主题之后,App在运行状态中去更新配色和素材,这也是适配暗黑模式的难点所在,而且对于开发者来说,工作量无疑是巨大的。

苹果提供的适配方案主要有两个:

1、将两种主题不同的素材直接存储在对象中,UIKit在主题变化时获取对应的素材更新展示。

优点:工作量相对较少,对开发者比较友好
缺点:灵活性差

2、给出主题变化的通知,让开发者在主题变化的通知回调里面做相应的适配工作。

优点:高度自定义,灵活性非常强
缺点:适配工作工作量巨大
对于不同的适配方案,开发者需要根据应用的实际情况去选择相应的适配方案,下面分别针对两种方案做一下实际适配:

1.1 颜色的适配
1.1.1 使用UIKIt提供的动态颜色
- (UILabel *)textLabel {
    if (!_textLabel) {
        _textLabel = [[UILabel alloc] initWithFrame:CGRectMake(37, 50, kLabelWidth, kLabelHeight)];
        _textLabel.backgroundColor = [UIColor lightGrayColor];
        _textLabel.layer.cornerRadius = 30.0;
        _textLabel.clipsToBounds = YES;
        _textLabel.text = @"使用UIKit提供的动态颜色";
        _textLabel.textAlignment = NSTextAlignmentCenter;
        if (@available(iOS 13.0, *)) {
            _textLabel.textColor  = UIColor.secondaryLabelColor;
        }else {
            _textLabel.textColor = [UIColor redColor];
        }
        _textLabel.font = [UIFont systemFontOfSize:16.0 weight:UIFontWeightMedium];
    }
    return _textLabel;
}

secondaryLabelColor、labelColor等这些颜色都内置了对两套主题的适配,在iOS13下,UIKit 提供的视图组件的背景颜色、文字颜色等属性都是适配过两种主题的颜色,创建的视图组件即使没有手动设置颜色,也是已经适配过两种主题的。

1.1.2 创建动态颜色
- (UILabel *)textLabel1 {
    if (!_textLabel1) {
        _textLabel1 = [[UILabel alloc] initWithFrame:CGRectMake(37, 50 + kLabelHeight + 20, kLabelWidth, kLabelHeight)];
        _textLabel1.backgroundColor = [UIColor lightGrayColor];
        _textLabel1.layer.cornerRadius = 30.0;
        _textLabel1.clipsToBounds = YES;
        _textLabel1.text = @"动态的创建颜色";
        _textLabel1.textAlignment = NSTextAlignmentCenter;
        if (@available(iOS 13.0, *)) {
            _textLabel1.textColor  = [UIColor ww_colorWithLightColor:[UIColor redColor] darkColor:[UIColor greenColor]];
        }else {
            _textLabel1.textColor = [UIColor redColor];
        }
        _textLabel1.font = [UIFont systemFontOfSize:16.0 weight:UIFontWeightMedium];
    }
    return _textLabel1;
}

/// 动态更换颜色的具体实现
+ (UIColor *)ww_colorWithLightColor:(UIColor *)lightColor darkColor:(UIColor *)darkColor {
#if __IPHONE_13_0
    if (@available(iOS 13.0, *)) {
        return [self colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
                switch (traitCollection.userInterfaceStyle) {
                    case UIUserInterfaceStyleDark:
                        return darkColor ?: lightColor;
                    case UIUserInterfaceStyleLight:
                    case UIUserInterfaceStyleUnspecified:
                    default:
                        return lightColor;
                }
            }];
        } else {
#endif
        return lightColor;
#if __IPHONE_13_0
        }
#endif
}
1.1.3 在assets里面添加动态颜色资源

在Xcode11里面可以把颜色当作一种资源添加到assets里面,一个颜色组可以有多个颜色,适配不同的主题模式。


image.png
- (UILabel *)textLabel2 {
    if (!_textLabel2) {
        _textLabel2 = [[UILabel alloc] initWithFrame:CGRectMake(37, 50 + kLabelHeight *2 + 20 *2, kLabelWidth, kLabelHeight)];
        _textLabel2.backgroundColor = [UIColor lightGrayColor];
        _textLabel2.layer.cornerRadius = 30.0;
        _textLabel2.clipsToBounds = YES;
        _textLabel2.text = @"在Assets中添加动态资源";
        _textLabel2.textAlignment = NSTextAlignmentCenter;
        if (@available(iOS 13.0, *)) {
            _textLabel2.textColor  = [UIColor colorNamed:@"labelColor"];
        }else {
            _textLabel2.textColor = [UIColor redColor];
        }
        _textLabel2.font = [UIFont systemFontOfSize:16.0 weight:UIFontWeightMedium];
    }
    return _textLabel2;
}
1.2 图片的适配
1.2.1 在assets中添加动态图片资源
image.png

在Xcode11里面,assets里面的一张图片除了根据scale分成三张外,还要根据主题分成三组,如果再根据是否是高对比度、颜色色域、布局方向,这么算下来配置一张图片就需要:33222 = 72张图片资源。

- (UIImageView *)imageView1 {
    if (!_imageView1) {
        _imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(150, 50 + kLabelHeight *3 + 20 *3, kSmallImageViewWH, kSmallImageViewWH)];
        _imageView1.image = [UIImage imageNamed:@"image"];
    }
    return _imageView1;
}
1.2.2 创建自己的动态图片
- (UIImageView *)imageView2 {
    if (!_imageView2) {
        _imageView2 = [[UIImageView alloc] initWithFrame:CGRectMake(196, 50 + kLabelHeight *3 + 20 *3, kSmallImageViewWH, kSmallImageViewWH)];
        _imageView2.image = [UIImage ww_imageWithLightImage:[UIImage imageNamed:@"image2_light"] darkImage:[UIImage imageNamed:@"image2_dark"]];
    }
    return _imageView2;
}

/// 创建动态图片的具体实现
+ (UIImage *)ww_imageWithLightImage:(UIImage *)lightImage darkImage:(UIImage *)darkImage {
    if (!lightImage) {
        return nil;
    }
#if __IPHONE_13_0
    if (@available(iOS 13.0, *)) {
        UITraitCollection *lightCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight];
        UITraitCollection *darkCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark];
        UITraitCollection *unspecifiedCollection = [UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight];
        UIImage *image = UIImage.new;
        UIImage *darkPure = [darkImage.imageAsset imageWithTraitCollection:unspecifiedCollection];
        UIImage *lightPure = [lightImage.imageAsset imageWithTraitCollection:unspecifiedCollection];
        [image.imageAsset registerImage:lightPure withTraitCollection:lightCollection];
        [image.imageAsset registerImage:darkPure withTraitCollection:darkCollection];
        [image.imageAsset registerImage:lightPure withTraitCollection:unspecifiedCollection];
        return image;
    } else {
#endif
        return lightImage;
#if __IPHONE_13_0
    }
#endif
}
1.2.3 网络图片

比如有这么一种场景:我浅色主题和深色主题下显示不同的图片,并且两张图片都不是本地的,都是从网络获取,实现思路大概是:先把两张图片下下来,在本地组装成动态图片

- (UIImageView *)imageView3 {
    if (!_imageView3) {
        _imageView3 = [[UIImageView alloc] initWithFrame:CGRectMake(45, 50 + kLabelHeight *3 + 20 *4 + kSmallImageViewWH, 280, kImageView3H)];
        _imageView3.backgroundColor = [UIColor lightGrayColor];
        _imageView3.layer.cornerRadius = 10.0;
        _imageView3.clipsToBounds = YES;
        
    }
    return _imageView3;
}

/// 具体实现方案
+ (void)ww_imageWithLightUrl:(NSURL *)lightUrl darkUrl:(NSURL * _Nullable)darkUrl completion:(void(^)(UIImage * _Nullable image, NSError * _Nullable error))completion {
    __block BOOL darkFinish = false;
    __block BOOL lightFinish = false;
    __block UIImage *lightImage;
    __block UIImage *darkImage;
    __block NSError *downloadError;
    void(^finishBlock)(void) = ^() {
        if (lightImage && darkImage) {
            UIImage *image = [UIImage ww_imageWithLightImage:lightImage darkImage:darkImage];
            completion(image, nil);
            return;
        }
        NSError *noImageError = [NSError errorWithDomain:@"com.WWDarkModeDemo.Remote" code:0 userInfo:@{@"message": @"图片为nil,请检查你的图片."}];
        completion(nil, noImageError);
       };
       finishBlock = [finishBlock copy];
    
       if (lightUrl == nil || darkUrl == nil) {
            NSError *error = [[NSError alloc] initWithDomain:@"com.WWDarkModeDemo.Extension" code:0 userInfo:@{@"message":@"浅色主题或深色主题的url不能为nil."}];
            completion(nil,error);
        }
    
      [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:lightUrl completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
          lightFinish = YES;
          downloadError = error;
          lightImage = image;
          finishBlock();
      }];
    
      [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:darkUrl completed:^(UIImage * _Nullable image, NSData * _Nullable data, NSError * _Nullable error, BOOL finished) {
           darkFinish = YES;
           downloadError = error;
           darkImage = image;
           finishBlock();
      }];
}
2.1 UITraitCollection是什么?

UITraitCollection是用来处理苹果手机的一些特性的存储和UI相关的配置 比如我修改了某些系统设置,如:改字体大小

+ (UITraitCollection *)traitCollectionWithPreferredContentSizeCategory:(UIContentSizeCategory)preferredContentSizeCategory API_AVAILABLE(ios(10.0));
@property (nonatomic, copy, readonly) UIContentSizeCategory preferredContentSizeCategory API_AVAILABLE(ios(10.0)); // unspecified: UIContentSizeCategoryUnspecified

关于主题模式切换的属性是我们本节关注的重点:

typedef NS_ENUM(NSInteger, UIUserInterfaceStyle) {
    UIUserInterfaceStyleUnspecified,
    UIUserInterfaceStyleLight,
    UIUserInterfaceStyleDark,
} API_AVAILABLE(tvos(10.0)) API_AVAILABLE(ios(12.0)) API_UNAVAILABLE(watchos);

+ (UITraitCollection *)traitCollectionWithUserInterfaceStyle:(UIUserInterfaceStyle)userInterfaceStyle API_AVAILABLE(tvos(10.0)) API_AVAILABLE(ios(12.0)) API_UNAVAILABLE(watchos);
@property (nonatomic, readonly) UIUserInterfaceStyle userInterfaceStyle API_AVAILABLE(tvos(10.0)) API_AVAILABLE(ios(12.0)) API_UNAVAILABLE(watchos); // unspecified: UIUserInterfaceStyleUnspecified
2.2 通过子类重写traitCollectionDidChange这个方法来监听系统设置的一些属性变化
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
    [super traitCollectionDidChange:previousTraitCollection];
//    NSLog(@"previousTraitCollection - %ld \n self.traitCollection - %ld",previousTraitCollection.userInterfaceStyle,self.traitCollection.userInterfaceStyle);
    if (@available(iOS 13.0, *)) {
        if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
            if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                self.textLabel.textColor = [UIColor orangeColor];
            }else {
                self.textLabel.textColor = [UIColor yellowColor];
            }
        }
    }
    
//    UIColor *labelTextColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traitCollection) {
//        if (traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
//            return [UIColor orangeColor];
//        } else {
//            return [UIColor yellowColor];
//        }
//    }];
//
//    self.textLabel.textColor = labelTextColor;
}

三、Q&A

Q:如何让应用不支持暗黑模式?
A:info.plist里面添加属性User Interface Stylelight

Q:如何让应用的某些页面不支持暗黑模式?
A:

// 设置当前页面的主题模式 就不跟随系统设置模式的变化而变化
 self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;

Q:系统主题更换之后会调用哪些方法?
A:
在ViewController:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
- (void)updateViewConstraints
- (void)viewWillLayoutSubviews
- (void)viewDidLayoutSubviews

在View里:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
- (void)drawRect:(CGRect)rect
- (void)layoutSubviews
- (void)updateConstraints
- (void)tintColorDidChange

在UIPresentationController里:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection
- (void)containerViewWillLayoutSubviews
- (void)containerViewDidLayoutSubviews

四、结尾

最好的资料在官方文档:

https://developer.apple.com/documentation/xcode/supporting_dark_mode_in_your_interface?changes=latest_minor

WWDC视频:https://developer.apple.com/videos/play/wwdc2019/214

相关文章

  • 关于iOS13 DarkMode的适配那些事儿

    一、为什么要调研这个暗黑模式的适配? 在2020年3月4日,苹果粑粑可能心情大好发了这么一个公告: 在这个公告里面...

  • 暗黑模式开发

    iOS13暗黑模式适配(项目开发版) iOS 13 DarkMode 暗黑模式 IOS 暗黑模式适配---基础适配

  • uni-app做iOS的夜间模式

    iOS13适配暗黑模式/夜间模式/深色模式/暗黑主题(DarkMode)问题:1.监听Android深色或浅色模式...

  • DarkMode、WKWebView、苹果登录是否必须适配?

    级别:★☆☆☆☆标签:「DarkMode、WKWebView、苹果登录是否必须适配?」「DarkMode 是否适配...

  • iOS13 DarkMode适配(二)

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

  • iOS13 DarkMode适配(一)

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

  • iOS13 DarkMode适配(Swift)

    封装颜色管理类 使用方式:(深色模式切换时,系统会重新渲染颜色,自动在设置好的两种颜色中进行切换)

  • iOS13 适配问题 看这一篇就够了

    技术参考: apple login IOS13适配-详细 iOS 13 适配(持续更新中) iOS13适配 掘金 ...

  • iOS 13适配

    技术参考: apple login IOS13适配-详细 iOS 13 适配(持续更新中) iOS13适配 掘金 ...

  • iOS 关于iOS13那些事

    本文记录一些关于iOS 13的内容,欢迎指正和补充! 一、关于iOS13适配 1.关于一些私有属性的适配,iOS ...

网友评论

      本文标题:关于iOS13 DarkMode的适配那些事儿

      本文链接:https://www.haomeiwen.com/subject/ipsrehtx.html