《iOS 13 Dark Mode 设计和适配》

一、设计

1. 为什么需要Dark Mode:

  1. 让用户更好的Focus于App内展现的内容
  2. 在暗光环境下减少用户视觉疲劳,减少对周围他人影响
  3. OLED屏幕可以大幅降低屏幕耗电量
  4. 用户对深色UI的热衷
  5. 在不大刀阔斧的改动UI界面的前提下,为用户带来耳目一新的视觉体验

2. iOS的设计团队为Dark Mode设立了以下五点设计原则:

  1. 保持视觉风格的熟悉感
  2. 平台一致性
  3. 清晰的信息架构
  4. 辅助功能
  5. 易于开发者采用

3. Submit Your iPhone Apps to the App Store

iOS 13 is now running on 77% of all iOS devices introduced in the last four years, worldwide. Deliver great user experiences by seamlessly integrating with Dark Mode, Sign in with Apple, and the latest advances in ARKit 3, Core ML 3, and Siri. Starting April 30, 2020, all iPhone apps submitted to the App Store must be built with the iOS 13 SDK or later.
Take advantage of Xcode features such as storyboards (including launch storyboards), Auto Layout, and SwiftUI, to ensure your app’s interface elements and layouts automatically fit the display of all iPhone models, regardless of size or aspect ratio. Starting April 30, 2020, all apps submitted to the App Store must use an Xcode storyboard to provide the app’s launch screen and all iPhone apps must support all iPhone screens.

  • 针对苹果官方3月4日的公告,分析如下:
    1. 从2020年4月30日起,所有提交给App Store的 iPhone应用程序都必须使用iOS 13 SDK或更高版本构建。(必须使用Xcode 11或更高版本打包上架。)
    2. 从2020年4月30日起,所有提交给App Store的应用程序都必须使用 storyboard 来提供应用程序的启动屏幕。
    3. 所有iPhone应用程序都必须支持所有iPhone屏幕。

4. 微信适配了暗黑模式(2020.03.09)

二、适配

1. 启动页:

从2020年4月30日起,所有提交给App Store的应用程序都必须使用 storyboard 来提供应用程序的启动屏幕。

方法一:
  • 在 storyboard 中拖入UIImageView控件,设置好约束。


  • Assets.xcassets 中新建 New Image Set。默认新建的Image是不支持多种机型的,打开该Image的 Contents.json 文件,复制下面的代码覆盖该文件:
    "filename"对应的value替换为本地对应的图片名称即可(_dark结尾的图片对应的是暗黑模式)。
{
  "images" : [
    {
      "idiom" : "iphone",
      "scale" : "1x"
    },
    {
      "idiom" : "iphone",
      "scale" : "1x",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ]
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_4.png",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_4_dark.png",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ]
    },
    {
      "idiom" : "iphone",
      "subtype" : "retina4",
      "scale" : "1x"
    },
    {
      "idiom" : "iphone",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "subtype" : "retina4",
      "scale" : "1x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_5.png",
      "subtype" : "retina4",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_5_dark.png",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "subtype" : "retina4",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "subtype" : "retina4",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "subtype" : "retina4",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_7P.png",
      "subtype" : "736h",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_7P_dark.png",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "subtype" : "736h",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_7.png",
      "subtype" : "667h",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_7_dark.png",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "subtype" : "667h",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_X.png",
      "subtype" : "2436h",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_X_dark.png",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "subtype" : "2436h",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_Xs Max.png",
      "subtype" : "2688h",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_Xs Max_dark.png",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "subtype" : "2688h",
      "scale" : "3x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_XR.png",
      "subtype" : "1792h",
      "scale" : "2x"
    },
    {
      "idiom" : "iphone",
      "filename" : "iPhone_XR_dark.png",
      "appearances" : [
        {
          "appearance" : "luminosity",
          "value" : "dark"
        }
      ],
      "subtype" : "1792h",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

  • 在 LaunchScreen.storyboard 中选择自己创建的image。
  • 存在缓存问题(待解决)
方法二:

对于简单的启动图页面,直接在 LaunchScreen.storyboard 中自己画。创建icon、label等控件,设置好约束,就不用担心屏幕适配问题了。

2. UITraitCollection:

在iOS13中,我们可以通过UITraitCollection来判断当前系统所处的模式。UIScreen, UIWindow, UIViewController, UIPresentationController, UIView 都遵循了 UITraitEnvironment 协议。

/*! Trait environments expose a trait collection that describes their environment. */
@protocol UITraitEnvironment <NSObject>
@property (nonatomic, readonly) UITraitCollection *traitCollection API_AVAILABLE(ios(8.0));

/*! To be overridden as needed to provide custom behavior when the environment's traits change. */
- (void)traitCollectionDidChange:(nullable UITraitCollection *)previousTraitCollection API_AVAILABLE(ios(8.0));
@end

当用户更改系统外观时,系统会自动要求每个窗口和视图重绘自身。在此过程中,系统将为macOS和iOS调用下表中列出的几种众所周知的方法来更新您的内容。系统在调用这些方法之前会更新特征环境,所以只有在下列方法中获取 traitCollection 属性才是准确的。

如果您在这些方法之外进行外观敏感的更改,则您的应用可能无法针对当前环境正确绘制其内容。

在 iOS 13中,UIView、UIViewController 、UIWindow 有了一个 overrideUserInterfaceStyle 的新属性,可以覆盖系统的模式。

单个页面或视图关闭暗黑模式,设置 overrideUserInterfaceStyle 为对应的模式,强制限制该视图与其子视图以设置的模式进行展示,不跟随系统模式改变进行改变。

self.overrideUserInterfaceStyle = UIUserInterfaceStyleLight;

设置此属性会影响当前view/viewController/window 以及它下面的任何内容。

如果你希望一个子视图监听系统的模式,请将 overrideUserInterfaceStyle 属性设置为.unspecified。

3. 颜色:

  • 系统的语义化颜色


iOS默认提供了9个彩色色板(TintColor),在iOS 13中为了保证深色模式下的对比度效果,每个TintColor都新增了深浅模式两种版本,在调用时也应使用语义化的颜色名称,比如SystemBlue,在浅色模式下指的是#007AFF,在深色模式下则是#0A84FF。


  • 自定义颜色: 通过 Assets.xcassets
    Assets.xcassets 中新建 New Color Set。通过[UIColor colorNamed:@""] 方法使用。

  • 通过代码自定义颜色:

+ (UIColor *)colorWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider;
- (UIColor *)initWithDynamicProvider:(UIColor * (^)(UITraitCollection *))dynamicProvider;

4. 图片:

  • SF Symbols


SF Symbols 是iOS 13中引入的一项非常重要的新特性,由于Dark Mode下所有图标都会需要两套颜色,使用静态的图片切图会让图片素材数量激增,因此苹果干脆做了这一整套1500多个图标的矢量图标库,配合iOS中的基底层与架高层、语义化颜色、Vibrancy(鲜亮化)等动态的颜色处理,使用SF Symbols可以在深浅模式中都能自动获得完美的展示效果。


SF Symbols的原理和Iconfont很类似,都是将SVG矢量图形以Unicode字符的形式打包在字体文件中。SF Symbols是内置集成在苹果目前的系统默认字体San Francisco字体系列里的,开发者只需引用Symbol的名称就可以迅速调用出SF Symbols提供的图标。同时设计师也可以利用SF Symbols官方提供的SVG模板制作自定义的图标共App调用。
为您的应用创建自定义符号图像

+ (nullable UIImage *)imageNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle withConfiguration:(nullable UIImageConfiguration *)configuration
  • 自定义图片: 通过 Assets.xcassets
    Assets.xcassets 中新建 New Image Set。通过[UIImage imageNamed:@""] 方法使用。

5. 模糊效果:

在 iOS 13 中,我们需要让这些模糊效果随着系统模式的切换而切换。UIKit 提供了新的模糊样式且是动态的,会随着系统模式的改版而自动匹配。

利用 UIVisualEffectView 来创建一些类似模糊的效果时,不要设置类似 UIBlurEffectStyleExtraLight 这样带有明确颜色的效果,而是设置 UIKit 中新提供的动态样式的效果,比如 UIBlurEffectStyleSystemThinMaterialLight 或 UIBlurEffectStyleSystemThinMaterialDark。

UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemMaterial];
UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];

6. webView页面:

多数流行的浏览器新版本都已经支持了“prefers-color-scheme”参数来检测系统当前的外观是浅色还是深色模式。配合利用类似Semantic Color的方法来定义网页样式表中同一个颜色在深浅两种模式下的色值,Web内容也可以获得同原生App一样的自动适配深浅模式效果。


Dark Mode Support in WebKit


7. 封装适配工具:

针对iOS13暗黑模式的适配工作,写了几个分类,方便项目统一管理适配工作,可以通过 pod 'QYTheme' 导入到项目中。
功能如下:

    1. UIWindow+QYTheme.h
      当 UIUserInterfaceStyle 发生变化时的通知,可用于无法准确获取 TraitCollection 的地方
if (@available(iOS 13.0, *)) {
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(qy_doSomething:)
                                                 name:QYUserInterfaceStyleWillChangeNotification
                                               object:nil];
}  
    1. UIColor+QYTheme.h
      统一处理颜色的分类,iOS13以前取 anyColor
/// 获取动态颜色
/// @param anyColor 默认颜色
/// @param darkColor 暗黑颜色
+ (UIColor *)qy_setColorWithAny:(UIColor *)anyColor dark:(UIColor *)darkColor;
// 使用方法:
self.view.backgroundColor = [UIColor qy_setColorWithAny:[UIColor whiteColor] dark:[UIColor blackColor]];
    1. UIImageView+QYTheme.h
      统一处理图片的分类,可处理本地和网络图片,网络图片依赖 SDWebImage 框架。
/// 设置图片 [处理本地图片]
/// @param anyImage 默认图片
/// @param darkImage 暗黑图片
- (void)qy_setImageWithAny:(UIImage *)anyImage darkIamge:(UIImage *)darkImage;

/// 设置图片 [处理网络图片]
/// @param anyUrl 默认url
/// @param darkUrl 暗黑图片url
/// @param placeholder 通用占位图
- (void)qy_setImageWithURL:(NSURL *)anyUrl darkImageWithURL:(NSURL *)darkUrl placeholderImage:(UIImage *)placeholder;

/// 设置图片 [处理网络图片]
/// @param anyUrl 默认url
/// @param placeholder 默认占位图
/// @param darkUrl 暗黑图片url
/// @param darkPlaceholder 暗黑占位图
- (void)qy_setImageWithURL:(NSURL *)anyUrl placeholderImage:(UIImage *)placeholder darkImageWithURL:(NSURL *)darkUrl darkPlaceholderImage:(UIImage *)darkPlaceholder;
// 使用方法:
// 本地图片
[self.imgView qy_setImageWithAny:[UIImage imageNamed:@"anyImage"] darkIamge:[UIImage imageNamed:@"darkImage"]];
// 网络图片
[self.imgView qy_setImageWithURL:[NSURL URLWithString:@"https://xxx"]
                darkImageWithURL:[NSURL URLWithString:@"https://xxx"]
                placeholderImage:[UIImage imageNamed:@"image"]];

目前工具正在完善,如有不足,欢迎指正~


源码放在GitHub上了,想查看的小伙伴可以->戳这里。


千里之行,始于足下。


参考文档:

写给设计师的指南:iOS 13 Dark Mode 深度解析
Apple Design Resources


Submit Your iPhone Apps to the App Store
Supporting Dark Mode in Your Interface
How To Adopt Dark Mode In Your iOS App
使用 QMUITheme 实现换肤并适配 iOS 13 Dark Mode

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

推荐阅读更多精彩内容

  • iOS 13终于引来了暗黑模式。 每当新特性的到来,iOS开发者们既紧张又有点小兴奋,怀揣着被虐的心态,让我们来看...
    koin447阅读 63,526评论 16 106
  • iOS 13 支持适配的机型 iPhone X、iPhone XR、iPhone XS、iPhone XS Max...
    你买票吗阅读 8,147评论 2 18
  • 最近公司业务需求要更换APP主题。最开始是一个地方一个地方去改,而且项目中很多老代码是用xib写的,习惯纯代码编程...
    抓鱼猫L阅读 6,116评论 0 10
  • 原文博客地址: iOS13适配深色模式(Dark Mode) 好像大概也许是一年前, Mac OS系统发布了深色模...
    iOS猿_员阅读 1,084评论 0 12
  • 从砸门发泄到大哭,到情绪稳定制定好计划,并且坚持实现。小目标已经实现了,今天可以完成,接着就是这个月底前要完成的事...
    树莓有点儿酸阅读 67评论 0 0