How to compute color contrast ratio between two UIColor instances
If building for WatchOS
where CoreImage
is not available or for whatever reason you do not want to rely on CoreImage
and therefore CIColor
is unavailable, replace @Mobile Dan's luminance
function with:
private func luminance() -> CGFloat {
// https://www.w3.org/TR/WCAG20-TECHS/G18.html#G18-tests
func adjust(colorComponent: CGFloat) -> CGFloat {
return (colorComponent < 0.03928) ? (colorComponent / 12.92) : pow((colorComponent + 0.055) / 1.055, 2.4)
}
var red: CGFloat = 0
var green: CGFloat = 0
var blue: CGFloat = 0
var alpha: CGFloat = 0
getRed(&red, green: &green, blue: &blue, alpha: &alpha)
return 0.2126 * adjust(colorComponent: red)
+ 0.7152 * adjust(colorComponent: green)
+ 0.0722 * adjust(colorComponent: blue)
}
UIColor
extension with contrast ratio and luminance
The following UIColor
extension includes a static and instance contrast ratio method. A bonus luminance method is included since it is used by the static contrastRatio(between:and:)
method.
import UIKit
extension UIColor {
static func contrastRatio(between color1: UIColor, and color2: UIColor) -> CGFloat {
// https://www.w3.org/TR/WCAG20-TECHS/G18.html#G18-tests
let luminance1 = color1.luminance()
let luminance2 = color2.luminance()
let luminanceDarker = min(luminance1, luminance2)
let luminanceLighter = max(luminance1, luminance2)
return (luminanceLighter + 0.05) / (luminanceDarker + 0.05)
}
func contrastRatio(with color: UIColor) -> CGFloat {
return UIColor.contrastRatio(between: self, and: color)
}
func luminance() -> CGFloat {
// https://www.w3.org/TR/WCAG20-TECHS/G18.html#G18-tests
let ciColor = CIColor(color: self)
func adjust(colorComponent: CGFloat) -> CGFloat {
return (colorComponent < 0.04045) ? (colorComponent / 12.92) : pow((colorComponent + 0.055) / 1.055, 2.4)
}
return 0.2126 * adjust(colorComponent: ciColor.red) + 0.7152 * adjust(colorComponent: ciColor.green) + 0.0722 * adjust(colorComponent: ciColor.blue)
}
}
Example Use
// static method
let contrastRatio1 = UIColor.contrastRatio(between: UIColor.black, and: UIColor.white)
print(contrastRatio1) // 21.0
// instance method
let contrastRatio2 = UIColor.black.contrastRatio(with: UIColor.white)
print(contrastRatio2) // 21.0
Note
Following these links:
- https://www.w3.org/TR/css-color-4/#predefined
- https://github.com/dequelabs/axe-core/issues/1629#issuecomment-509880306
For predefinite colorspace (like in iOS see this https://developer.apple.com/videos/play/wwdc2016/712/) and also in general the correct THRESHOLD value is 0.04045 and not 0.03928 (read more)