简体   繁体   English

如何使用我的应用程序中使用的自定义颜色轻松支持明暗模式?

[英]How do I easily support light and dark mode with a custom color used in my app?

Let's say I have a custom color in my app:假设我的应用程序中有自定义颜色:

extension UIColor {
    static var myControlBackground: UIColor {
        return UIColor(red: 0.3, green: 0.4, blue: 0.5, alpha: 1)
    }
}

I use this in a custom control (and other places) as the control's background:我在自定义控件(和其他地方)中使用它作为控件的背景:

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
}

With iOS 13, I wish to have my custom control support both light and dark mode.在 iOS 13 中,我希望我的自定义控件同时支持明暗模式。

One solution is to override traitCollectionDidChange and see if the color has changed and then update my background as needed.一种解决方案是覆盖traitCollectionDidChange并查看颜色是否已更改,然后根据需要更新我的背景。 I also need to provide both a light and dark color.我还需要提供浅色和深色。

So I update my custom colors:所以我更新了我的自定义颜色:

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

And I update my control code:我更新了我的控制代码:

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
            }
        }
    }
}

This seems to work but it's clunky and anywhere else I happen to use myControlBackground needs to have the same code added.这似乎有效,但它很笨拙,我碰巧使用myControlBackground其他任何地方都需要添加相同的代码。

Is there a better solution to having my custom color and control support both light and dark mode?是否有更好的解决方案让我的自定义颜色和控件同时支持明暗模式?

As it turns out, this is really easy with the new UIColor init(dynamicProvider:) initializer.事实证明,使用新的UIColor init(dynamicProvider:)初始值设定项,这真的很容易。

Update the custom color to:将自定义颜色更新为:

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

That's it.就是这样。 No need to define two separate statics.无需定义两个单独的静态。 The control class doesn't need any changes from the original code.控件类不需要对原始代码进行任何更改。 No need to override traitCollectionDidChange or anything else.无需覆盖traitCollectionDidChange或其他任何内容。

The nice thing about this is that you can see the color change in the app switcher immediately after changing the mode in the Settings app.这样做的好处是,在“设置”应用程序中更改模式后,您可以立即在应用程序切换器中看到颜色变化。 And of course the color is up-to-date automatically when you go back to the app.当然,当您返回应用程序时,颜色会自动更新。

On a related note when supporting light and dark mode - Use as many of the provided colors from UIColor as possible.在支持明暗模式时的相关说明 - 尽可能多地使用 UIColor 提供的颜色。 See the available dynamic colors from UI Elements and Standard Colors .UI ElementsStandard Colors查看可用的动态颜色 And when you need your own app-specific colors to support both light and dark mode, use the code in this answer as an example.当您需要自己的应用程序特定颜色来支持明暗模式时,请使用此答案中的代码作为示例。


In Objective-C, you can define your own dynamic colors with:在 Objective-C 中,您可以定义自己的动态颜色:

UIColor+MyApp.h: UIColor+MyApp.h:

@interface UIColor (MyApp)

@property (class, nonatomic, readonly) UIColor *myControlBackgroundColor;

@end

UIColor+MyApp.m: 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];
    }
}

Another solution with iOS 13 is to define custom colors in your asset catalog using Xcode's asset editor. iOS 13 的另一个解决方案是使用 Xcode 的资产编辑器在资产目录中定义自定义颜色。

As mentioned in the documentation , when you need a specific color, create it as a color asset.文档中所述,当您需要特定颜色时,请将其创建为颜色资产。 In your asset, specify different color values for both light and dark appearances.在您的资产中,为浅色深色外观指定不同的颜色值。 You can also specify high-contrast versions of your colors.您还可以指定颜色的高对比度版本。

在此处输入图片说明

Note that Any Appearance variant is the color that is displayed on older systems that do not support Dark Mode.请注意,任何外观变体是在不支持暗模式的旧系统上显示的颜色。

To load a color value from an asset catalog, you can load the color by its name:要从资产目录加载颜色值,您可以按名称加载颜色:

// iOS
let aColor = UIColor(named: "customControlColor")

// macOS
let aColor = NSColor(named: NSColor.Name("customControlColor"))

Now anytime user switches between dark and light mode, the specified colors will dynamically change through the app.现在,无论何时用户在暗模式和亮模式之间切换,指定的颜色都会通过应用程序动态改变。

Here I got this helper method to create dynamic colors:在这里,我得到了这个帮助方法来创建动态颜色:

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 }
    }
}

And for the solution in the question, the helper method should be used as the following:对于问题中的解决方案,应使用辅助方法如下:

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

No need to override traitCollectionDidChange , just set the backgroundColor once and you are done.无需覆盖traitCollectionDidChange ,只需设置一次backgroundColor即可完成。

In case you are want to create dynamic colours programmatically:如果您想以编程方式创建动态颜色:

Reusable extension:可重复使用的扩展:

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
      }
   }
}

App Colors:应用颜色:

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

Usage:用法:

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()
   }

}

Result:结果:

光 黑暗的


UPDATE : NSColor extension:更新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.

相关问题 如何检测 AppDelegate 中的暗/亮模式更改以重置全局 tintColor? - How do I detect dark/light mode changes in my AppDelegate in order to reset global tintColor? 如何在深色模式和浅色模式之间更改文本颜色 - How to change text color between dark mode and light mode 如何在 iOS 的原生反应中动态地将亮模式更改为暗模式? - how do I dynamically change light mode to dark mode in react native in iOS? 如何根据暗/亮模式设置默认 label 颜色(在 Swift 中) - How to set default label color based on dark/light mode (in Swift) Swift:如果 traitCollection.userInterfaceStyle == .unspecified,我如何确定它是处于亮模式还是暗模式? - Swift: If traitCollection.userInterfaceStyle == .unspecified, how do I determine if it's in light or dark mode? 如何支持现有 swift 应用 iOS 的暗模式支持? - How to support support dark mode for existing swift app iOS? Swift 在应用程序内强制使用暗/亮模式 - Swift force dark/light mode inside an app SwiftUI:获取动态背景颜色(暗模式或亮模式) - SwiftUI: Get the Dynamic Background Color (Dark Mode or Light Mode) 我们如何在 iOS 13 的当前 iOS 应用中支持暗模式? - How we Support Dark Mode in Current iOS App with iOS 13? 即使整个应用程序处于暗模式,如何使用 SFSymbol 强制某些 UIImageView 处于亮模式? - How to enforce certain UIImageView with SFSymbol to be in light mode even though the entire app is in dark mode?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM