根据苹果的⼀贯保持的⻛格,我们会特别简单、舒服的适配了它。 因此,苹果介绍的就是这三句句话。
Dark mode is a new look
It's easy to implement
Flexible and powerful
根据苹果的设计思路,适配darkmode就是通过UIKit提供的基础对象进行颜色和图片的控制 ,一些系统提供的控件已经适配了dark mode
,但是还是有很多需要我们去适配的部分,开启我们的漫漫适配路吧~
如何切换mode
除了可以在setting->develop->Dark Appearance切换模式,Xcode 11增加了一个 辅助功能,可以帮助我们快速切换模拟器和storyboard或者xib的mode模式
Assets适配
颜色:创建一个颜色的Assets,调整Appearance为Any,Dark 然后就可以在里面配置不同Appearance的颜色啦~在assets添加自定义颜色是从iOS11开始的所以对老版本的iOS兼容不好 ,建议使用代码适配颜色 图片:assets的适配图片不会影响老的版本,老版本的iOS会自动识别 Any Appearance! 听到这里是不是超开心呢代码适配
- 系统的语义颜色以及系统颜色:这两种iOS13提供的动态颜色都会根据mode变化,帮助我们快速实现类似系统配色的适配,这里简单列举,大家感受一下~
LabelColor :文本颜色
secondaryLabelColor : 辅助内容的文本标签颜色
tertiaryLabelColor :三级
linkColor: 超链接标签颜色
separatorColor/opaqueseparatorColor:分隔符(细边框或者分割线)
systembackgroundColor :界面背景色 - 创建
Dynamic Color
UIColor *dyColor = [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull trainCollection) {
if ([trainCollection userInterfaceStyle] == UIUserInterfaceStyleLight) {
return lightColor;
}else {
return darkColor;
}
}];
关于图片的部分请继续往下看哦~
UITraitCollection
iOS trait环境
简介
通过UITraitEnvironment协议的traitCollection属性公开。
UIScreen、UIWindow 、UIViewController、UIPresentationController、 UIView遵守了 UITraitEnvironment协议。
根据这些属性的变化自适应界面的布局、设置特殊属性:UITraitCollection(水平大小,垂直大小 展示的比列 用户界面习惯用法)
当我们设置颜色的时候发现是同样的systemBackgroundColor,但两边是不一样的颜色 原因是系统做了层级处理 ,这里的层级处理
也是包含在UITraitCollection里面的UserInterfaceLevel
,在显示颜色变化的基础上,做了一些透明度等其他的一些处理
其实dynamicColor就是通过trait获取可以获取当前的展示的具体是什么颜色
let dynamic color = UIColor.systemBackGround
let traitCollection = view. traitCollection
let resolvedColor = dynamicColor.resolvedColor(with: traitCollection)
重写drawRect方法的时候 ,UIkit会把当前的环境设置给view的traitCollection,这个时候用systemcolor绘制会使用正确的颜色
,模式变化的时候会调用setNeedsDisplay触发drawRect方法
在下面方法处理的渲染是准确的
官方表示layout方法是我们使用traitcolletion的最好时机,把外观代码放到任何一个即可,不要做不必要的工作,可以调用他们的补充方法如 setNeedsUpdateConstraints、setNeedsLayout触发它们
但有些情况需特殊处理,比如CALayer 和 CGColor,他们不能识别darkMode, 我们需要通过traitcollection来处理
(1)通过CALayer添加上的view拿到它的traitcollection 获取当前需要展示的颜色 然后转成CGColor
(2)在performAsCurrent方法里面直接拿到当前的CGColor
(3)根据当前view的traitCollection判断,有时候view的traitCollection不一定和traitCollection.current保持一致,我们需要把当前的保存好赋值完成后设回去
知道mode的变化才能更好地控制我们需要改变的东西,mode变化会走的方法,横屏等操作也会走这个方法,建议先
判断是否是颜色的变化
图片和颜色不一样的地方是图片没有颜色的动态处理mode,那么我们需要自己处理通过imageAsset来获取当前图片的具体展示
+(UIImage *)getdarkModeImage:(NSString *)imgName
traitcollection:(UITraitCollection *)trait{
if (@available(iOS 13.0, *)) {
UIImage * imgs = [UIImage imageNamed:imgName inBundle:nil compatibleWithTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleLight]];
[imgs.imageAsset registerImage:[UIImage imageNamed:[NSString stringWithFormat:@"%@_dark",imgName]] withTraitCollection:[UITraitCollection traitCollectionWithUserInterfaceStyle:UIUserInterfaceStyleDark]];
return [imgs.imageAsset imageWithTraitCollection:trait];
}else{
return [UIImage imageNamed:imgName];
}
}
常规来看大家以为traitCollection是一个单利,大家根据他去做事情,但其实traitcollection贯穿了整个app,每个层级都有自己的raitcollection,每个层级根据自己的traitcollection做事情
当我们添加一个view的时候,它不知道怎么展示,addsubview的时候会将上一层的trait collection继承过来,就可以进行自己的mode的展示了
Xcode有一个的Debug 的功能 可以告诉我们
谁的traitcollection变化了
假如我们想某个页面保持黑夜状态 那么我们可以在
重写那个页面的traitcollection
,那么它下面的页面也都是dark mode了
设置某个页面忽略系统模式
let darkView = UIView()
darkView.overrideUserInterfaceStyle = .dark(影响里面的层级结构)
viewWithCustomAppearance.overrideUserInterfaceStyle = .unspecified /设置回去
要使整个app一直保持黑色或者白色 在info.plist文件里面设置UIUserInterfaceStyle为light或者dark
高斯模糊效果的设置
UIBlurEffect *effect = [UIBlurEffect effectWithStyle:UIBlurEffectStyleSystemThinMaterial];
UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect];
//放大 UIVisualEffectView 视图下面的内容的颜色,同时让UIVisualEffectView视图的contentView看起来e更生动
UIVibrancyEffect * vibrancyeffect = [UIVibrancyEffect effectForBlurEffect:effect style:UIVibrancyEffectStyleSecondaryLabel];
UIVisualEffectView *vibrancyV = [[UIVisualEffectView alloc] initWithEffect:vibrancyeffect];
[effectView.contentView addSubview:vibrancyV];
富文本
设置attributes的foregroundColor设置为.label,不指定则默认为.black
NSMutableAttributedString *resultStr = [[NSMutableAttributedString alloc] initWithString: [NSString stringWithFormat: @"代金券(%@)",string]];
[resultStr addAttribute:NSFontAttributeName value:BU_FONT_14 range:NSMakeRange(0, 3)];
[resultStr addAttribute:NSForegroundColorAttributeName value:[UIColor generateDynamicColor:BU_COLOR_C1 darkColor:[UIColor whiteColor]] range:NSMakeRange(0,3)];//attribute的foregroundColor(VIP)
[resultStr addAttribute:NSFontAttributeName value:BU_FONT_14 range:NSMakeRange(3, resultStr.length - 3)];
[resultStr addAttribute:NSForegroundColorAttributeName value:BUColorWithHex(0xf81c46) range:NSMakeRange(3, resultStr.length - 3)];
return resultStr;
iOS13新变化
StatusBar
:之前分为default 和lightContent两种类型 ,现在darkcontent表示default样式下的light模式下的导航栏样式,现在default变成了dynamic类型,根据mode变化
UIActivityIndicatorView
:去掉了之前的各种类型 ,现在是设置大小 ,颜色属性自己控制
webcontent
macOS 10.14.4中的Safari 12.1更新,WebKit中的暗模式支持已经到来。Safari和WebKit不会自动使网页内容变暗 ,内容支持暗模式的主要方式是采用color-scheme中指定的新样式属性。
查询通过CSS变量指定颜色值
:root {
color-scheme: light dark;
--special-text-color: hsla(60, 100%, 50%, 0.5);
--border-color: black;
}
@media (prefers-color-scheme: dark) {
:root {
--special-text-color: hsla(60, 50%, 70%, 0.75);
--border-color: white;
}
}
.special {
color: var(--special-text-color);
border: 1px solid var(--border-color);
}
调整顺序建议 :xib -> storyboard->code ->富文本->画图方法,assets的图片只能用imageNamed的方式加载对内存不友好,但暗黑适配可以省去超多麻烦,同时assets的使用也有超多好处[ WWDC2018 ] - 优化 App Assets Optimizing App Assets 建议小的常用图片assets适配 ,大的或者网络图片代码适配 ,assets对颜色暗黑适配的iOS老版本不友好,最好还是代码适配