Recently, the company’s business needs to change the APP theme. In the beginning, I changed it one place at a time. Moreover, many old codes in the project were written in XIB, which made it very difficult for me to change as I was accustomed to pure code programming. And you may have to change the theme again later.

So I defined macros for a few major colors, and I used macros wherever I set colors in my code. This only needs to be changed once, and you can change the macro directly when you want to switch themes.

Combined with the function of switching theme already done, plus A dark mode judgment, if the current dark mode is A color value, if not B color value, so that the dark mode ADAPTS.

.

A defined macro:

When you set colors in your code, you use predefined colors. (The following macros are only used in my project scenarios, not applicable to all apps, can be customized for your own project. Some colors don’t change between the two modes.

#define CKDarkMode @available(iOS 13.0, *) && UITraitCollection.currentTraitCollection.userInterfaceStyle == UIUserInterfaceStyleDark // MARK: - hexadecimal color #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: - Use a global variable to set the background and text, so that you can elegantly switch themes (use globally unique names for easy maintenance; #define Color_Bg CKDarkMode? HexOf(0x191C32):HexOf(0xf4F4F4) // Background Theme color Black/White #define Color_ContView CKDarkMode? #define Color_Title CKDarkMode :HexOf(0x1D213B):HexOf(0xFFFFFF) // Contents, cell color dark blue/white #define Color_Title CKDarkMode #define Color_Subtitle CKDarkMode? #define Color_Subtitle CKDarkMode? HexOf(0x999999):HexOf(0x999999) #define Color_Green CKDarkMode? HexOf(0x45C98F):HexOf(0x45C98F) #define Color_Red CKDarkMode? // #define Color_NavBg CKDarkMode? // #define Color_NavBg CKDarkMode? Hex (0x1D213B):HexOf(0xffffff) // #define Color_TabbarBg CKDarkMode? HexOf(0x1D213B):HexOf(0xFFFFFF) #define Color_Selected CKDarkMode? 0x46CA8F :HexOf(0x46CA8F) :HexOf(0x46CA8F) #define Color_Line CKDarkMode? HexOf(0x191C32):HexOf(0xf4F4F4) #define Color_darkdarkMode? HexOf(0x333333):HexOf(0x333333) // #define Color_Gray CKDarkMode? HexOf(0x666666):HexOf(0x666666) // Grey #define Color_LightGray CKDarkMode? HexOf(0x999999):HexOf(0x999999) // Light grey #define Color_InputBg CKDarkMode? HexOf(0x191C32):HexOf(0xf4F4F4) // Input box Background color #define Color_DarkBlue CKDarkMode? HexOf(0x191C32):HexOf(0x191C32) // Dark blue (special color) #define Color_HalfTitle CKDarkMode? X999999 Hex_A (0, 0.5) : Hex_A (0 x999999, 0.5); // The color value of the translucent text is half that of the subtitleCopy the code

To turn off Dark mode, set it to:

#define CKDarkMode NO

.

Ii. Problems encountered:

1. I used it at firstself.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDarkSome classes do not have the UITraitCollection attribute, so many errors are reported.

Solution:

Switch to UITraitCollection. Current attributes for the current App color mode.

CGColorRef correlation:

bt.layer.borderColor = Color_Selected.CGColor;
Copy the code

Error:

Incompatible operand types (‘UIColor * _Nonnull’ and ‘CGColorRef _Nonnull’ (aka ‘struct CGColor *’))

Solution:

UIColor *color = Color_Selected;
bt.layer.borderColor = color.CGColor;
Copy the code

3. The normal mode can be displayed every time you open the APP; However, if you switch mode after opening the APP, the page that has been loaded will still display the theme mode before switching.

Solution: Add notifications to the page, refresh the page color when you get notifications to switch themes (similar to the international notification processing logic in your project)

4. The status bar of some pages in the project is fixed in white. When switching pages, the status bar will be switched back to the theme color of black.

Solution: Add a UIStatusBarStyle variable to record the topic status bar color without making too much extra judgment in the controller. If you use @available(iOS 13.0, *), you need to change the code everywhere after the requirements change. This way, you can change the theme or turn off dark mode without having to change the code. This can also be determined by the macro CKDarkMode defined above. To turn off dark mode, just set CKDarkMode to NO

UIStatusBarStyle _themeStatusBarStyle; - (void)viewWillAppear:(BOOL) Animated {[super viewWillAppear:animated]; _themeStatusBarStyle = [UIApplication sharedApplication].statusBarStyle; / / set the status bar color to white [UIApplication sharedApplication]. StatusBarStyle = UIStatusBarStyleLightContent; } - (void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated]; // Restore the status bar color to the theme color [UIApplication sharedApplication]. StatusBarStyle = _themeStatusBarStyle; }Copy the code

5. Where macros are used, a warning will be sent to remind me to do system version judgment, but actually I have already judged in CKDarkMode, and the system cannot detect:

‘currentTraitCollection’ is only available on iOS 13.0 or newer

Solution: Use the UIColor extension. The 999+ warning affects the visual experience of the code a little bit, it should be extended in the future. If you have a better solution please leave a comment below.

.

Iii. Introduction to UITraitCollection

1. In iOS 13, we can judge the mode of the current system by UITraitCollection.UIView and UIViewController, UIScreen, UIWindowI’ve compliedUITraitEnvironmentSo these classes all have a protocol calledtraitCollectionIn these classes, we can judge the color mode of the current App as follows:

BOOL isDark = (self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark);

2. In addition, we can also passUITraitCollection.currentProperty to get the current App color mode.

3. If you don’t want to open this feature for the time being, you can turn off dark mode globally for the time being:

In the info.plist file, add key to User Interface Style, type to String, and value to Light (Dark). If you open it again, delete this setting. (This turns off dark mode for the entire life of the APP; The Settings above #define CKDarkMode NO are just code judgments and only work where macros are used).

4. In iOS 13,UIView, UIViewController, UIWindowHave aoverrideUserInterfaceStyleNew property to override the system’s schema.

A. a single page or view closed mode, set the overrideUserInterfaceStyle to the corresponding model, forced to limit the view and its view in the setting mode to display, do not follow the system model change to change.

self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;

Setting this property affects the current view/viewController/window and anything below it. If you want a child to view surveillance system model, please send overrideUserInterfaceStyle attribute is set to. Unspecified.

.

Fourth, expand

In addition to my implementation, there are other ways to adapt to dark mode:

1. UIColor extension:

+(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; }}Copy the code

Problem: This method requires a normal mode color and a dark mode color for each use, which is not easy to maintain.

Solution: You can define several common color functions, but only for special scenarios, so that you don’t need to control the color values everywhere.

+(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; }}Copy the code

2, Can be inImages.xcassetsDefines several common colors and configures a corresponding color for dark mode:

3, inImages.xcassetsWhen you set it to Dark mode, it will automatically display the corresponding image

4. Startup Diagram:

Launchscreen.storyboard allows you to set another image for dark mode just like normal images

5, layer:

UIColor *resolvedColor = [[UIColor labelColor] resolvedColorWithTraitCollection:self.view.traitCollection];
label.layer.borderColor = resolvedColor.CGColor;
Copy the code

6, UIActivityIndicatorView style:

IOS UIActivityIndicatorViewStyle before 13:

typedef NS_ENUM(NSInteger, UIActivityIndicatorViewStyle) {
    UIActivityIndicatorViewStyleWhiteLarge,
    UIActivityIndicatorViewStyleWhite,
    UIActivityIndicatorViewStyleGray,
};
Copy the code

After iOS 13, the above three attributes are deprecated due to the dark mode. We recommend using the following 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", 
};
Copy the code

7, Status Bar style:

Before the iOS 13, the style of the status bar of the enumeration values also has the obvious tendency of color, UIStatusBarStyleDefault, UIStatusBarStyleLightContent.

The default style of the status bar now displays different colors based on the current mode, while the original lightContent style has a darkContent style to match it.

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
};
Copy the code

Eight,SF Symbols

.

Note:

Ensure that the name is globally unique to avoid the same name as other names in the project. This ensures that the global search results are only what you want to search for, which is easy to maintain. For example, if you use the name RedColor, you will find a lot of other useless information. This naming idea can also be used in other places.

In addition to changing the background color and text color, you also need to replace ICONS and images, which require the UI to work with cutting images.

(Apps adapted to Diablo mode: Mobile Taobao, Wechat Reading, QQ Music, netease Cloud Music, IQiyi, Zhihu, netease News, Baidu Tieba)

Reference:

How To Adopt Dark Mode In Your iOS App

DarkMode1

DarkMode2

DarkMode3