![](/img/trans.png)
[英]How do I detect dark/light mode changes in my AppDelegate in order to reset global tintColor?
[英]How do I easily support light and dark mode with a custom color used in my app?
假設我的應用程序中有自定義顏色:
extension UIColor {
static var myControlBackground: UIColor {
return UIColor(red: 0.3, green: 0.4, blue: 0.5, alpha: 1)
}
}
我在自定義控件(和其他地方)中使用它作為控件的背景:
class MyControl: UIControl {
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
backgroundColor = .myControlBackground
}
// Lots of code irrelevant to the question
}
在 iOS 13 中,我希望我的自定義控件同時支持明暗模式。
一種解決方案是覆蓋traitCollectionDidChange
並查看顏色是否已更改,然后根據需要更新我的背景。 我還需要提供淺色和深色。
所以我更新了我的自定義顏色:
extension UIColor {
static var myControlBackgroundLight: UIColor {
return UIColor(red: 0.3, green: 0.4, blue: 0.5, alpha: 1)
}
static var myControlBackgroundDark: UIColor {
return UIColor(red: 0.4, green: 0.3, blue: 0.2, alpha: 1)
}
}
我更新了我的控制代碼:
extension MyControl {
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if #available(iOS 13.0, *) {
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
backgroundColor = traitCollection.userInterfaceStyle == .dark ?
.myControlBackgroundDark : .myControlBackgroundLight
}
}
}
}
這似乎有效,但它很笨拙,我碰巧使用myControlBackground
其他任何地方都需要添加相同的代碼。
是否有更好的解決方案讓我的自定義顏色和控件同時支持明暗模式?
事實證明,使用新的UIColor init(dynamicProvider:)
初始值設定項,這真的很容易。
將自定義顏色更新為:
extension UIColor {
static var myControlBackground: UIColor {
if #available(iOS 13.0, *) {
return UIColor { (traits) -> UIColor in
// Return one of two colors depending on light or dark mode
return traits.userInterfaceStyle == .dark ?
UIColor(red: 0.5, green: 0.4, blue: 0.3, alpha: 1) :
UIColor(red: 0.3, green: 0.4, blue: 0.5, alpha: 1)
}
} else {
// Same old color used for iOS 12 and earlier
return UIColor(red: 0.3, green: 0.4, blue: 0.5, alpha: 1)
}
}
}
就是這樣。 無需定義兩個單獨的靜態。 控件類不需要對原始代碼進行任何更改。 無需覆蓋traitCollectionDidChange
或其他任何內容。
這樣做的好處是,在“設置”應用程序中更改模式后,您可以立即在應用程序切換器中看到顏色變化。 當然,當您返回應用程序時,顏色會自動更新。
在支持明暗模式時的相關說明 - 盡可能多地使用 UIColor 提供的顏色。 從UI Elements和Standard Colors查看可用的動態顏色。 當您需要自己的應用程序特定顏色來支持明暗模式時,請使用此答案中的代碼作為示例。
在 Objective-C 中,您可以定義自己的動態顏色:
UIColor+MyApp.h:
@interface UIColor (MyApp)
@property (class, nonatomic, readonly) UIColor *myControlBackgroundColor;
@end
UIColor+MyApp.m:
+ (UIColor *)myControlBackgroundColor {
if (@available(iOS 13.0, *)) {
return [UIColor colorWithDynamicProvider:^UIColor * _Nonnull(UITraitCollection * _Nonnull traits) {
return traits.userInterfaceStyle == UIUserInterfaceStyleDark ?
[self colorWithRed:0.5 green:0.4 blue:0.2 alpha:1.0] :
[self colorWithRed:0.3 green:0.4 blue:0.5 alpha:1.0];
}];
} else {
return [self colorWithRed:0.3 green:0.4 blue:0.5 alpha:1.0];
}
}
iOS 13 的另一個解決方案是使用 Xcode 的資產編輯器在資產目錄中定義自定義顏色。
如文檔中所述,當您需要特定顏色時,請將其創建為顏色資產。 在您的資產中,為淺色和深色外觀指定不同的顏色值。 您還可以指定顏色的高對比度版本。
請注意,任何外觀變體是在不支持暗模式的舊系統上顯示的顏色。
要從資產目錄加載顏色值,您可以按名稱加載顏色:
// iOS
let aColor = UIColor(named: "customControlColor")
// macOS
let aColor = NSColor(named: NSColor.Name("customControlColor"))
現在,無論何時用戶在暗模式和亮模式之間切換,指定的顏色都會通過應用程序動態改變。
在這里,我得到了這個幫助方法來創建動態顏色:
extension UIColor {
static func dynamicColor(light: UIColor, dark: UIColor) -> UIColor {
guard #available(iOS 13.0, *) else { return light }
return UIColor { $0.userInterfaceStyle == .dark ? dark : light }
}
}
對於問題中的解決方案,應使用輔助方法如下:
extension UIColor {
static let myControlBackground: UIColor = dynamicColor(light: UIColor(red: 0.3, green: 0.4, blue: 0.5, alpha: 1), dark: UIColor(red: 0.4, green: 0.3, blue: 0.2, alpha: 1))
}
無需覆蓋traitCollectionDidChange
,只需設置一次backgroundColor
即可完成。
如果您想以編程方式創建動態顏色:
可重復使用的擴展:
extension UIColor {
public class func dynamicColor(light: UIColor, dark: UIColor) -> UIColor {
if #available(iOS 13.0, *) {
return UIColor {
switch $0.userInterfaceStyle {
case .dark:
return dark
default:
return light
}
}
} else {
return light
}
}
}
應用顏色:
struct MyColors {
///> This is what you are getting from designers,
/// in case they are not providing consistent color naming.
/// Can be also just strings with HEX-codes.
static let xF9EFB1 = #colorLiteral(red: 0.9764705882352941, green: 0.9372549019607843, blue: 0.6941176470588235, alpha: 1)
static let x6A6A6A = #colorLiteral(red: 0.4156862745098039, green: 0.4156862745098039, blue: 0.4156862745098039, alpha: 1)
static let xFEFEFE = #colorLiteral(red: 0.9960784313725490, green: 0.9960784313725490, blue: 0.9960784313725490, alpha: 1)
static let x202020 = #colorLiteral(red: 0.1254901960784314, green: 0.1254901960784314, blue: 0.1254901960784314, alpha: 1)
///<
static var myLabelForeground: UIColor {
return UIColor.dynamicColor(light: MyColors.x6A6A6A, dark: MyColors.xF9EFB1)
}
static var myViewBackground: UIColor {
return UIColor.dynamicColor(light: MyColors.xFEFEFE, dark: MyColors.x202020)
}
}
用法:
class SampleView: View {
private lazy var label = Label(text: "Hello!")
override func setupUI() {
label.textColor = MyColors.myLabelForeground
label.font = UIFont.systemFont(ofSize: 24, weight: .semibold)
backgroundColor = MyColors.myViewBackground
addSubview(label)
LayoutConstraint.centerXY(label).activate()
}
}
結果:
更新: NSColor
擴展:
import AppKit
extension NSColor {
public class func dynamicColor(light: NSColor, dark: NSColor) -> NSColor {
if #available(OSX 10.15, *) {
return NSColor(name: nil) {
switch $0.name {
case .darkAqua, .vibrantDark, .accessibilityHighContrastDarkAqua, .accessibilityHighContrastVibrantDark:
return dark
default:
return light
}
}
} else {
return light
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.