iOS夜间模式开发探索(iOS13)

1、背景

1.1 iOS13苹果推出了dark mode模式,很多系统内置应用都支持了dark mode模式,大家可以安装一下beta版本看一下,就连地图应用都支持了dark mode模式、大部分我们自己开发app如果使用地图的话基本上都是使用第三方的sdk,比如百度地图,高德地图,如果三方地图不支持dark mode,我们app的dark mode就会显得很鸡肋,dark mode 官方说对OLED屏幕可以增加电池续航,除此之外开发者app支持的好坏决定这个功能是否鸡肋。
1.2 对于dark mode的支持苹果爸爸官方是什么态度呢,

Supporting both light and dark appearances is a good practice, but you might have good reasons to opt out of appearance changes wholly or partially in your app. Views containing user-created content should always reflect the user’s choices. Similarly, you might choose a specific appearance for print-related views so that they reflect what the user sees on the printed page
The system assumes that apps linked against the iOS 13 or later SDK support both light and dark appearances. In iOS, you specify the specific appearance you want by assigning a specific interface style to your window, view, or view controller. You can also disable support for Dark Mode entirely using an Info.plist key.

The system automatically opts in any app linked against the iOS 13.0 or later SDK to both light and dark appearances. If you need extra time to work on your app's Dark Mode support, you can temporarily opt out by including the UIUserInterfaceStyle key (with a value of Light) in your app’s Info.plist file. Setting this key to Light causes the system to ignore the user's preference and always apply a light appearance to your app.

Important
Supporting Dark Mode is strongly encouraged. Use the UIUserInterfaceStyle key to opt out only temporarily while you work on improvements to your app's Dark Mode support.

苹果爸爸说了这个功能是被强烈建议的,默认是需要你支持的,如果你时间不够的话,就可以先在plist里配置暂时不支持dark mode,除非你的应用真的不需要支持,比如打印界面,以后如果不支持会不会被拒都不好说,建议开发者未雨绸缪吧。

2.技术支持

对于dark mode的支持,其实有两种方法可以实现(目前还不明确不用系统的API支持dark mode是否可以):
2.1、在苹果出来黑暗模式之前,就有很多app支持夜间模式,尤其是阅读类的app. 在Github上有一个三方的库专门支持夜间模式,星星数还挺多,大家可以看一下DKNightVersion
2.2、本节的重点就是来介绍一下iOS13系统的API对dark mode的支持
首先我们简单说一下对webContent的Dark mode的支持【WWDC视频链接地址】,中文的webcontent深色模式适配可以参考博客:为你的网页添加深色模式--点我
2.3 iOS13原生API的支持:

2.3.1对颜色的支持:
在iOS13官方增加了dynamic color,动态颜色就是在dark mode和light mode模式下其值是不一样的, 而且模式切换后,系统会给你重新界面渲染。大家可以看一下官方UIColor类文件对颜色的定义。
对颜色的支持主要有两种方式,第一种比较简单:

iOS夜间模式开发探索(iOS13)_第1张图片
颜色在color set里设置

在上面添加一个color set, 然后用代码就可以获取dynamic color了

 UIColor *dynamicColor = [UIColor colorNamed:@"dynamicClolor"];

第二种就是Choose semantic colors instead of fixed color values.主要区分了1、System colors 2、 Foreground colors 3、Background colors 4、Fill colors 5、Other colors

  • System colors 包含了system开头的一些颜色, 一定要注意这些颜色的色值根据背景纹理会是不同的!
  • Foreground colors包含了一些要展示的颜色,包含四个级别的labelColor和linkColor以及placeholderTextColor、separatorColor,opaqueSeparatorColor
  • Background colors包含了三个级别的systemBackgroundColor 和三个级别的systemGroupedBackgroundColor
  • Fill colors包含了四个级别的systemFillColor
  • Other colors包含了lightTextColor,darkTextColor这些color不会随着模式的更改而更改

如果你们打算使用苹果官方给出的颜色进行app开发,您可以参考下官方对颜色的使用说明,也可以参考
这篇文章关于新增iOS 13 Dark Mode API的那些事

iOS夜间模式开发探索(iOS13)_第2张图片
light mode和dark mode颜色展示对比

当然苹果给出了这五种类型的颜色不能满足我们设计的小贼眼,所以苹果官方给出了自己生成dynamic color的API:

@interface UIColor (DynamicColors)

/* Create a dynamic color with a provider.
* When methods are called on this color that need color component values,
* the provider is called with UITraitCollection.currentTraitCollection.
* The provider should use that trait collection to decide a more fundamental UIColor to return.
* As much as possible, use the given trait collection to make that decision, not other state.
*/

+ (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);

/* Resolve any color to its most fundamental form (a non-dynamic color) for a specific trait collection.
*/
- (UIColor *)resolvedColorWithTraitCollection:(UITraitCollection *)traitCollection API_AVAILABLE(ios(13.0), tvos(13.0)) API_UNAVAILABLE(watchos);
@end
============================================================================================================================
//dynamic color API example:
       UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
           if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
               return [UIColor blackColor];
           }
           else {
               return [UIColor whiteColor];
           }
       }];
        [self addLabelWithSuperView:scrollView andtext:[NSString stringWithFormat:@"%@",@"dyColor"] andframe:CGRectMake(15, 10, 160, 30) andtag:10 andTextcolor:dyColor];

============================================================================================================================

2.3.2对图片的支持(Providing Images for Different Appearances):

iOS夜间模式开发探索(iOS13)_第3张图片
Asset完美的支持了黑暗模式,但是放在bundle里的图片如何命名也支持黑暗模式呢?这个就不知道有没有方法了,如果大家有知道的,可以给我留言

2.3.3对毛玻璃效果的支持

对毛玻璃效果的支持
@interface UIVibrancyEffect (AdditionalStyles)
//+ (UIVibrancyEffect *)effectForBlurEffect:(UIBlurEffect )blurEffect style:(UIVibrancyEffectStyle)style API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos);
@end
/

* Blur styles available in iOS 13.
*
* Styles which automatically adapt to the user interface style:
/
UIBlurEffectStyleSystemUltraThinMaterial API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemThinMaterial API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemMaterial API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemThickMaterial API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemChromeMaterial API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
/
And always-light and always-dark versions:
*/
UIBlurEffectStyleSystemUltraThinMaterialLight API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemThinMaterialLight API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemMaterialLight API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemThickMaterialLight API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemChromeMaterialLight API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemUltraThinMaterialDark API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemThinMaterialDark API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemMaterialDark API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemThickMaterialDark API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),
UIBlurEffectStyleSystemChromeMaterialDark API_AVAILABLE(ios(13.0)) API_UNAVAILABLE(tvos, watchos),

iOS夜间模式开发探索(iOS13)_第4张图片
DEB15BD622EDD6C4FCAC418E7D106745.png

2.3.4:默认系统的控件,像tabbar,navigationbar,slide, 对黑暗模式是支持的。
2.3.5:dark mode change some method will be called ,like these:

iOS夜间模式开发探索(iOS13)_第5张图片
5BC300852AF81235C8DCF044EA6B5F4F.png

2.3.6: Resolving Dynamic Colors

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) { // Resolve dynamic colors again
} }

Resolving Dynamic Colors
let dynamicColor = UIColor.systemBackground
let traitCollection = view.traitCollection
let resolvedColor = dynamicColor.resolvedColor(with: traitCollection)

let layer = CALayer()
let traitCollection = view.traitCollection
// Option 1
let resolvedColor = UIColor.label.resolvedColor(with: traitCollection)
layer.borderColor = resolvedColor.cgColor

let layer = CALayer()
let traitCollection = view.traitCollection
// Option 2
}
traitCollectionperformAsCurrent{
layer.boaderColor = UIcolor.label.cgclolor
}

let layer = CALayer()
let traitCollection = view.traitCollection
// Option 3
UITraitCollection.current = traitCollection
layer.borderColor = UIColor.label.cgColor

Resolving Dynamic Images
let image = UIImage(named: "HeaderImage")
let asset = image?.imageAsset
let resolvedImage = asset?.image(with: traitCollection)

2.3.7:Choosing a Specific Interface Style for Your iOS App

Override the Interface Style for a Window, View, or View Controller

When your interface must always appear in a light or dark style, regardless of the system setting, set the overrideUserInterfaceStyle property of the appropriate window, view, or view controller to that style. Overriding the interface style affects other objects in your interface as follows:

  • View controllers—The view controller’s views and child view controllers adopt the style.

  • Views—The view and all of its subviews adopt the style.

  • Windows—Everything in the window adopts the style, including the root view controller and all presentation controllers that display content in that window.

The following code example enables a light appearance for a view controller and all of its views.

    override func viewDidLoad() {
        super.viewDidLoad()

        // Always adopt a light interface style.    
        overrideUserInterfaceStyle = .light
    }

Override the Interface Style for Child View Controllers

Parent view controllers control the appearance of their contained child view controllers. To override the interface style for a child view controller, use the setOverrideTraitCollection:forChildViewController: method to assign new traits to that view controller. For custom presentations, you can similarly override the interface style of the presented view controller by assigning new traits to the overrideTraitCollection property of your UIPresentationController object.

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

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

2.3.9:获取当前模式

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

2.3.10模式切换时打印系统log日志
在Xcode菜单栏Product->Scheme->Edit Scheme
选择Run->Arguments->Arguments Passed On Launch
添加以下命令即可
-UITraitCollectionChangeLoggingEnabled YES

总结:Use UIKit colors, materials, views, and controls Customize colors and images when necessary

==================================================================================
参考:

1、https://developer.apple.com/documentation/appkit/supporting_dark_mode_in_your_interface?language=objc

2、Choosing a Specific Interface Style for Your iOS App

3、https://developer.apple.com/videos/play/wwdc2019/214

4、关于新增iOS 13 Dark Mode API的那些事 https://www.jianshu.com/p/83e7fa45b0a2

5、https://blog.csdn.net/ios8988/article/details/92422826

你可能感兴趣的:(iOS夜间模式开发探索(iOS13))