web-dev-qa-db-fra.com

Comment puis-je facilement prendre en charge le mode clair et sombre avec une couleur personnalisée utilisée dans mon application?

Disons que j'ai une couleur personnalisée dans mon application:

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

J'utilise ceci dans un contrôle personnalisé (et d'autres endroits) comme arrière-plan du contrôle:

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
}

Avec iOS 13, je souhaite que mon contrôle personnalisé prenne en charge le mode clair et le mode sombre.

Une solution consiste à remplacer traitCollectionDidChange et à voir si la couleur a changé, puis à mettre à jour mon arrière-plan au besoin. Je dois également fournir une couleur claire et foncée.

Je mets donc à jour mes couleurs personnalisées:

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

Et je mets à jour mon code de contrôle:

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

Cela semble fonctionner mais c'est maladroit et partout où j'utilise myControlBackground doit avoir le même code ajouté.

Existe-t-il une meilleure solution pour que ma couleur et mon contrôle personnalisés prennent en charge le mode clair et le mode sombre?

9
rmaddy

Il s'avère que c'est vraiment facile avec le nouvel initialiseur UIColor init(dynamicProvider:).

Mettez à jour la couleur personnalisée pour:

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

C'est tout. Pas besoin de définir deux statiques distinctes. La classe de contrôle n'a besoin d'aucune modification par rapport au code d'origine. Pas besoin de remplacer traitCollectionDidChange ou autre chose.

La bonne chose à ce sujet est que vous pouvez voir le changement de couleur dans le sélecteur d'application immédiatement après avoir changé le mode dans l'application Paramètres. Et bien sûr, la couleur est mise à jour automatiquement lorsque vous revenez à l'application.

Sur une note connexe lors de la prise en charge du mode clair et sombre - Utilisez autant de couleurs fournies par UIColor que possible. Voir les couleurs dynamiques disponibles de I Elements et Standard Colors . Et lorsque vous avez besoin de vos propres couleurs spécifiques à l'application pour prendre en charge le mode clair et sombre, utilisez le code de cette réponse comme exemple.


Dans Objective-C, vous pouvez définir vos propres couleurs dynamiques avec:

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];
    }
}
26
rmaddy

Une autre solution avec iOS 13 consiste à définir des couleurs personnalisées dans votre catalogue d'actifs à l'aide de l'éditeur d'actifs Xcode.

Comme mentionné dans la documentation , lorsque vous avez besoin d'une couleur spécifique, créez-la comme ressource couleur. Dans votre élément, spécifiez différentes valeurs de couleur pour les apparences clair et foncé. Vous pouvez également spécifier des versions à contraste élevé de vos couleurs.

enter image description here

Notez que Toute variante d'apparence est la couleur qui s'affiche sur les anciens systèmes qui ne prennent pas en charge le mode sombre.

Pour charger une valeur de couleur à partir d'un catalogue d'actifs, vous pouvez charger la couleur par son nom:

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

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

Désormais, chaque fois que l'utilisateur bascule entre le mode sombre et le mode clair, les couleurs spécifiées changeront dynamiquement via l'application.

6