美文网首页IOS知识整理
iOS13 暗黑模式(Dark Mode)适配(oc)

iOS13 暗黑模式(Dark Mode)适配(oc)

作者: 冰点雨 | 来源:发表于2020-03-12 16:36 被阅读0次

    目录

    1.适配暗黑模式(Dark Mode)
    1.1颜色适配
    * 系统动态颜色*
    * 自定义动态UIColor(代码自定义、xib自定义)*
    2.获取当前模式(Light or Dark)
    3. 不同模式的监听
    4. CGcolor适配
    5. 图片适配
    6. 其他

    1.适配暗黑模式(Dark Mode)

    1.1颜色适配

    iOS13 之前 UIColor只能表示一种颜色,而从 iOS13 开始UIColor是一个动态的颜色,在Light Mode和Dark Mode可以分别设置不同的颜色。
    iOS13系统提供了一些动态颜色

    @property (class, nonatomic, readonly) UIColor *systemBrownColor        API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *systemIndigoColor       API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *systemGray2Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *systemGray3Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *systemGray4Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *systemGray5Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *systemGray6Color        API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *labelColor              API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *secondaryLabelColor     API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *tertiaryLabelColor      API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *quaternaryLabelColor    API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *linkColor               API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *placeholderTextColor    API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *separatorColor          API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *opaqueSeparatorColor    API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    @property (class, nonatomic, readonly) UIColor *systemBackgroundColor                   API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *secondarySystemBackgroundColor          API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *tertiarySystemBackgroundColor           API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *systemGroupedBackgroundColor            API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *secondarySystemGroupedBackgroundColor   API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *tertiarySystemGroupedBackgroundColor    API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *systemFillColor                         API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *secondarySystemFillColor                API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *tertiarySystemFillColor                 API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    @property (class, nonatomic, readonly) UIColor *quaternarySystemFillColor               API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchOS);
    

    1.1.1 系统动态颜色使用

    [self.view setBackgroundColor:[UIColor systemBackgroundColor]];
    [self.titleLabel setTextColor:[UIColor labelColor]];
    [self.detailLabel setTextColor:[UIColor placeholderTextColor]];
    

    1.1.2 自定义动态UIColor

    在实际开发过程,系统提供的这些颜色还远远不够,因此我们需要创建更多的动态颜色

    初始化UIcolor的方法

    + (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    - (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchOS);
    

    这两个方法需要传一个block
    当系统在LightModeDarkMode之间相互切换时就会触发此回调
    这个block会返回一个UITraitCollection
    我们需要使用其属性userInterfaceStyle,它是一个枚举类型,会告诉我们当前是LightMode还是DarkMode

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

    使用

    UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
            if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
                return [UIColor redColor];
            }
            else {
                return [UIColor greenColor];
            }
        }];
     [self.bgView setBackgroundColor:dyColor];
    

    实际开发中颜色适配的使用

    纯代码开发

    iOS13之后苹果将UIColor改变成了动态颜色,UIColor会根据不同模式来改变不同的颜色。系统也提供了初始化方法。
    接下来是我自己对UIColor进行的category,这里提供了一些设置颜色的方法

    以下代码是category类UIColor+XY.h

    //UIColor+XY.h
    
    #import <UIKit/UIKit.h>
    
    @interface UIColor (XY)
    
    /// 十六进制字符串获取颜色
    /// @param color 16进制色值  支持@“#123456”、 @“0X123456”、 @“123456”三种格式
    + (UIColor *)colorWithHexString:(NSString *)color;
    
    /// 十六进制字符串获取颜色
    /// @param color 16进制色值  支持@“#123456”、 @“0X123456”、 @“123456”三种格式
    /// @param alpha 透明度
    + (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha;
    
    /// 适配暗黑模式颜色   传入的UIColor对象
    /// @param lightColor 普通模式颜色
    /// @param darkColor 暗黑模式颜色
    + (UIColor *)colorWithLightColor:(UIColor *)lightColor DarkColor:(UIColor *)darkColor;
    
    /// 适配暗黑模式颜色   颜色传入的是16进制字符串
    /// @param lightColor 普通模式颜色
    /// @param darkColor 暗黑模式颜色
    + (UIColor *)colorWithLightColorStr:(NSString *)lightColor DarkColor:(NSString *)darkColor;
    
    /// 适配暗黑模式颜色   颜色传入的是16进制字符串 还有颜色的透明度
    /// @param lightColor 普通模式颜色
    /// @param lightAlpha 普通模式颜色透明度
    /// @param darkColor 暗黑模式颜色透明度
    /// @param darkAlpha 暗黑模式颜色
    + (UIColor *)colorWithLightColorStr:(NSString *)lightColor WithLightColorAlpha:(CGFloat)lightAlpha DarkColor:(NSString *)darkColor WithDarkColorAlpha:(CGFloat)darkAlpha;
    @end
    

    category类UIColor+XY.m

    //
    //  UIColor+XY.m
    
    #import "UIColor+XY.h"
    
    @implementation UIColor (XY)
    
    ///十六进制字符串获取颜色
    /// @param color 16进制色值  支持@“#123456”、 @“0X123456”、 @“123456”三种格式
    + (UIColor *)colorWithHexString:(NSString *)color{
        return [self colorWithHexString:color alpha:1.0f];
    }
    
    
    /// 十六进制字符串获取颜色
    /// @param color 16进制色值  支持@“#123456”、 @“0X123456”、 @“123456”三种格式
    /// @param alpha 透明度
    + (UIColor *)colorWithHexString:(NSString *)color alpha:(CGFloat)alpha{
        //删除字符串中的空格
        NSString *cString = [[color stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] uppercaseString];
        // String should be 6 or 8 characters
        if ([cString length] < 6){
            return [UIColor clearColor];
        }
        // strip 0X if it appears
        //如果是0x开头的,那么截取字符串,字符串从索引为2的位置开始,一直到末尾
        if ([cString hasPrefix:@"0X"]){
            cString = [cString substringFromIndex:2];
        }
        //如果是#开头的,那么截取字符串,字符串从索引为1的位置开始,一直到末尾
        if ([cString hasPrefix:@"#"]){
            cString = [cString substringFromIndex:1];
        }
        if ([cString length] != 6){
            return [UIColor clearColor];
        }
        
        // Separate into r, g, b substrings
        NSRange range;
        range.location = 0;
        range.length = 2;
        //r
        NSString *rString = [cString substringWithRange:range];
        //g
        range.location = 2;
        NSString *gString = [cString substringWithRange:range];
        //b
        range.location = 4;
        NSString *bString = [cString substringWithRange:range];
        
        // Scan values
        unsigned int r, g, b;
        [[NSScanner scannerWithString:rString] scanHexInt:&r];
        [[NSScanner scannerWithString:gString] scanHexInt:&g];
        [[NSScanner scannerWithString:bString] scanHexInt:&b];
        return [UIColor colorWithRed:((float)r / 255.0f) green:((float)g / 255.0f) blue:((float)b / 255.0f) alpha:alpha];
    }
    
    
    
    /// 适配暗黑模式颜色   传入的UIColor对象
    /// @param lightColor 普通模式颜色
    /// @param darkColor 暗黑模式颜色
    + (UIColor *)colorWithLightColor:(UIColor *)lightColor DarkColor:(UIColor *)darkColor {
        if (@available(iOS 13.0, *)) {
            return [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
                if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
                    return lightColor;
                } else {
                    return darkColor;
                }
            }];
        } else {
            return lightColor ? lightColor : (darkColor ? darkColor : [UIColor clearColor]);
        }
    }
    
    /// 适配暗黑模式颜色   颜色传入的是16进制字符串
    /// @param lightColor 普通模式颜色
    /// @param darkColor 暗黑模式颜色
    + (UIColor *)colorWithLightColorStr:(NSString *)lightColor DarkColor:(NSString *)darkColor{
        return [UIColor colorWithLightColor:[UIColor colorWithHexString:lightColor] DarkColor:[UIColor colorWithHexString:darkColor]];
    }
    
    
    /// 适配暗黑模式颜色   颜色传入的是16进制字符串 还有颜色的透明度
    /// @param lightColor 普通模式颜色
    /// @param lightAlpha 普通模式颜色透明度
    /// @param darkColor 暗黑模式颜色透明度
    /// @param darkAlpha 暗黑模式颜色
    + (UIColor *)colorWithLightColorStr:(NSString *)lightColor WithLightColorAlpha:(CGFloat)lightAlpha DarkColor:(NSString *)darkColor WithDarkColorAlpha:(CGFloat)darkAlpha{
        return [UIColor colorWithLightColor:[UIColor colorWithHexString:lightColor alpha:lightAlpha] DarkColor:[UIColor colorWithHexString:darkColor alpha:darkAlpha]];
    }
    @end
    
    

    在pch文件添加宏定义

    ///适配暗黑模式   lightColor:白天模式颜色  darkColor:暗黑模式颜色
    #define KCustomAdjustColor(lightColor, darkColor) [UIColor colorWithLightColor:lightColor DarkColor:darkColor]
    

    使用

    //只需在给颜色赋值的时候指定不同模式下的颜色即可
    self.customColorLabel.backgroundColor = [UIColor colorWithLightColorStr:@"226597" DarkColor:@"5ACFB1"] ;
    

    xib开发

    对于使用xib的童鞋来说,我们就只能使用color set颜色集了。

    *新建Color Set *

    color.png

    *使用方法: *

    ///用ColorSet 跟UIImage使用是类似的  把自定义的颜色名字写入就行了
    self.colorSetLabel.textColor = [UIColor colorNamed:@"customBlueColor"];
    

    要进行CGColor的适配,需要先了解监听模式的切换

    2. 获取当前模式(Light or Dark)

    iOS13中CGColor依然只能表示单一的颜色
    通过调用UITraitCollection.currentTraitCollection.userInterfaceStyle
    获取当前模式

    if (UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
            [self.titleLabel setText:@"DarkMode"];
        }
        else {
            [self.titleLabel setText:@"LightMode"];
        }
    

    3. 监听模式的切换

    有时我们需要监听系统模式的变化,并作出响应
    那么我们就需要在需要监听的viewController中,重写下列函数

    // 注意:参数为变化前的traitCollection
    - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection;
    
    // 判断两个UITraitCollection对象是否不同
    - (BOOL)hasDifferentColorAppearanceComparedToTraitCollection:(UITraitCollection *)traitCollection;
    

    使用

    - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
        [super traitCollectionDidChange:previousTraitCollection];
        // trait发生了改变
        if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
        // 执行操作
        }
        }
    

    ⚠️⚠️⚠️⚠️⚠️⚠️⚠️特别注意:

    当用户更改外观时,系统会通知所有window与View需要更新样式,在此过程中iOS会触发以下方法完整的触发方法文档

    UIView

    traitCollectionDidChange(_:)
    layoutSubviews()
    draw(_:)
    updateConstraints()
    tintColorDidChange()
    

    UIViewController

    traitCollectionDidChange(_:)
    updateViewConstraints()
    viewWillLayoutSubviews()
    viewDidLayoutSubviews()
    

    UIPresentationController

    traitCollectionDidChange(_:)
    containerViewWillLayoutSubviews()
    containerViewDidLayoutSubviews()
    

    4. CGColor适配

    iOS13中UIColor是动态变化的,但是对于CGColr,CALayer层面上的Color来说,还是只是单一颜色,我们只能在模式变化的时候的监听方法中适配。

    方式一: resolvedColor

    // 通过当前traitCollection得到对应UIColor
    // 将UIColor转换为CGColor
    - (UIColor *)resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection;
    

    使用

    - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
        [super traitCollectionDidChange:previousTraitCollection];
        
        UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
            if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
                return [UIColor redColor];
            }
            else {
                return [UIColor greenColor];
            }
        }];
        UIColor *resolvedColor = [dyColor resolvedColorWithTraitCollection:previousTraitCollection];
        layer.backgroundColor = resolvedColor.CGColor;
    }
    

    方式二: performAsCurrent

    // 使用当前trainCollection调用此方法
    - (void)performAsCurrentTraitCollection:(void (^)(void))actions;
    

    使用

    - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
        [super traitCollectionDidChange:previousTraitCollection];
        
        UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
            if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
                return [UIColor redColor];
            }
            else {
                return [UIColor greenColor];
            }
        }];
        [self.traitCollection performAsCurrentTraitCollection:^{
            layer.backgroundColor = dyColor.CGColor;
        }];
        
    }
    

    方式三:最简单的方法

    直接设置为一个动态UIColor的CGColor即可

    /// 模式改变的回调代理
    - (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection{
        [super traitCollectionDidChange:previousTraitCollection];
        /**
         *CGColor适配
         *iOS13后,UIColor能够表示动态颜色,但是CGColor依然只能表示一种颜色。所以对于CALayer对象只能在traitCollectionDidChange方法中进行改变
        */
        if (@available(iOS 13.0, *)) {
            if ([self.traitCollection hasDifferentColorAppearanceComparedToTraitCollection:previousTraitCollection]) {
                //修改label的边框色
                self.layerLabel.layer.borderColor = KTestColor.CGColor;
                if (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark) {
                    NSLog(@"************* iOS13 暗黑模式");
                }else{
                    NSLog(@"############# iOS13 普通模式");
                }
            }
        }
    }
    

    ⚠️⚠️⚠️!!! 设置layer颜色都是在traitCollectionDidChange中,意味着如果没有发生模式切换,layer将会没有颜色,需要设置一个基本颜色

    5. 图片适配

    图片适配类似于color.seet的创建,首先创建一个image set ,设置模式,两种模式放入不同的图片

    使用

    [_logoImage setImage:[UIImage imageNamed:@"icon_logo"]];
    

    6. 其他

    1. 禁止暗黑模式,全局关闭暗黑模式

    1.在Info.plist文件中,添加UIUserInterfaceStyle key 名字为 User Interface Style 值为String,

    UIUserInterfaceStyle key 的值设置为Light

    如图


    image.png

    2. 模式切换时打印log

    模式切换时自动打印log,就不需要我们一次又一次的执行po命令了
    在Xcode菜单栏Product->Scheme->Edit Scheme
    选择Run->Arguments->Arguments Passed On Launch
    添加以下命令即可
    -UITraitCollectionChangeLoggingEnabled YES

    img.png

    3. 强行设置App模式

    当系统设置为Light Mode时,对某些App的个别页面希望一直显示Dark Mode下的样式,这个时候就需要强行设置当前ViewController的模式了

    // 设置当前view或viewCongtroller的模式
    @property(nonatomic) UIUserInterfaceStyle overrideUserInterfaceStyle;
    

    使用

    // 设置为Dark Mode即可
    [self setOverrideUserInterfaceStyle:UIUserInterfaceStyleDark];
    

    ⚠️ 注意!!!

    当我们强行设置当前viewControllerDark Mode后,这个viewController下的view都是Dark Mode
    由这个ViewController present出的ViewController不会受到影响,依然跟随系统的模式
    要想一键设置App下所有的ViewController都是Dark Mode,请直接在Window上执行overrideUserInterfaceStyle
    window.rootViewController强行设置Dark Mode也不会影响后续present出的ViewController的模式

    4. NSAttributedString优化

    对于UILabel、UITextField、UITextView,在设置NSAttributedString时也要考虑适配Dark Mode,否则在切换模式时会与背景色融合,造成不好的体验

    不建议

    NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16]};
    NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];
    

    建议

    // 添加一个NSForegroundColorAttributeName属性
    NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:16],NSForegroundColorAttributeName:[UIColor labelColor]};
    NSAttributedString *str = [[NSAttributedString alloc] initWithString:@"富文本文案" attributes:dic];
    

    相关文章

      网友评论

        本文标题:iOS13 暗黑模式(Dark Mode)适配(oc)

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