Dynamische Textfarbe für eine Hintergrundfarbe


Mit Einführung des Hell- und des Dunkelmodus in macOS und iOS wurde die Farbpalette von SwiftUI erweitert und angepasst. Viele der Standardfarben, beispielsweise blue oder red, verändern sich geringfügig in Abhängigkeit vom gewählten Modus. Dies passiert, um einen besseren Kontrast zu gewährleisten. Zusätzlich gibt es Farben, die sich komplett an den aktuell gewählten Modus anpassen. Ein schwarzer Text wird im Dunkelmodus weiß, wenn als Farbe zuvor nicht black, sondern primary verwendet wurde. Farben mit ähnlicher Funktionalität gibt es für den Hintergrund. Beim Umschalten zwischen Hell- und Dunkelmodus wechseln diese Farben automatisch.

Wann aber sollte ein Text schwarz oder weiß sein? Um einen guten Kontrast zu gewährleisten, ist die Textfarbe abhängig von der Hintergrundfarbe. Ein schwarzer Text auf einem dunkelgrauen Hintergrund ist ebenso schlecht lesbar wie eine weiße Schrift auf einem hellgrauen Hintergrund. Was aber ist mit mit blauen oder roten Hintergründen? Welche Textfarbe ist dort zu bevorzugen? Schwarz oder weiß? Auf diese Frage gibt es eine Antwort, denn das Ergebnis kann berechnet werden. Die Grundlage ist eine Formel, mit der die Leuchtdichte, im englischen bezeichnet als Luminance, einer Farbe ermittelt werden kann. Hat ein Hintergrund eine hohe Leuchtdichte, sollte eine dunkle Textfarbe verwendet werden. Bei einer geringen Leuchtdichte stattdessen eine helle Textfarbe.

In SwiftUI kann die Leuchtdichte aus den RGB-Komponenten einer Farbe ermittelt werden. Benötigt werden die Farbanteile von Rot, Grün und Blau sowie die folgende Formel:

Lumniance = 0,2126 * Rot + 0,7152 * Grün + 0,0722 * Blau

Das folgende Listing zeigt eine Erweiterung der Struktur Color. Die Funktion luminance ermittelt die Leuchtdichte auf Grundlage der gezeigten Formel. Die Funktion isLight prüft, ob es eine Farbe mit hoher Leuchtdichte ist. Das ist bei einem Wert von über 0,5 der Fall. Die Funktion adaptedTextColor gibt in Abhängigkeit von isLight eine schwarze oder oder weiße Farbe zurück. Weil es eine Extension von color ist, können diese Funktionen direkt auf jede beliebige Farbe angewendet werden. Beispielsweise auf die Hintergrundfarbe einer View. Die Funktion adaptedTextColor liefert dann die für den Hintergrund passende Textfarbe in Schwarz oder Weiß.

import SwiftUI

extension Color {
        
    func luminance() -> Double {
        
        var red : CGFloat = 0
        var green : CGFloat = 0
        var blue : CGFloat = 0
        
        let uiColor = UIColor(self)
        uiColor.getRed(&red, green: &green, blue: &blue, alpha: nil)
                
        return  0.2126 * Double(red) + 0.7152 * Double(green) + 0.0722 * Double(blue)
    }
    
    func isLight() -> Bool {
        return luminance() > 0.5
    }
    
    func adaptedTextColor() -> Color {
        return isLight() ? Color.black : Color.white
    }
}

Das nächste Listing ist ein Beispiel für eine SwiftUI View mit drei Schiebereglern (Slider). Mit Ihnen können die Farbanteile von rot, grün und blau einzeln gesteuert werden. Wie in der Apple Welt üblich sind es Werte zwischen 0 und 1. Die Funktion backgroundColor erzeugt aus den Farbanteilen eine neue Farbe, die für den Hintergrund der View verwendet wird. Zusätzlich kommt backgroundColor in Verbindung mit adaptedTextColor für einen Text in der View zum Einsatz. Unterschreitet die Leuchtdichte den Wert von 0,5, ändert sich die Textfarbe automatisch von schwarz auf weiß.

import SwiftUI

struct LuminanceView: View {
    
    @State var redSliderValue : Double = 0.7
    @State var greenSliderValue : Double = 0.7
    @State var blueSliderValue : Double = 0.7

    private func backgroundColor() -> Color {
        return Color(red: redSliderValue, green: greenSliderValue, blue: blueSliderValue)
    }
        
    var body: some View {
        
        VStack {
                        
            Slider(value: $redSliderValue, in: 0...1)
                .tint(.black)
                .padding()
            
            Slider(value: $greenSliderValue, in: 0...1)
                .tint(.black)
                .padding()
            
            Slider(value: $blueSliderValue, in: 0...1)
                .tint(.black)
                .padding()
            
            Text("Hello Luminance")
                .font(.largeTitle)
                .foregroundStyle(backgroundColor().adaptedTextColor())
            
            Spacer()
        }
        .background(backgroundColor())
    }
}
Stacks Image 71

Geschrieben am: 24.04.2024