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类文件对颜色的定义。
对颜色的支持主要有两种方式,第一种比较简单:

颜色在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的那些事

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):

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),

DEB15BD622EDD6C4FCAC418E7D106745.png

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 把月亮分出来 一杯敬过往的锈迹斑斑, 一杯放荡 凝重的灵魂无处安放 泥封的酒坛 剩下落雪,剩下秋天的叹息 不忍打碎...
    罗子阅读 393评论 8 39
  • 昨天送别一位同事。场面比较热烈,同事很聪明,很勤劳,在工会和部门工作中都极其出类拔萃,让大家非常不舍。 正好10年...
    靠谱陈老师阅读 601评论 1 48
  • 记住,懒惰的人是无法在2014年称霸世界的! 无论身处哪个行业,不论世界如何变迁,只要您积极的迎合不断变化的消费者...
    东风知我阅读 401评论 0 1
  • 今日,雨过天晴,阳光灿烂。骑行在路上,穿梭在这小城的风里,一路上看到好几条可爱的小狗狗,有黑色,白色,黄色,但是它...
    爱若是花阅读 420评论 2 3