├── ColorContrast.playground ├── Contents.swift ├── contents.xcplayground └── timeline.xctimeline └── README.md /ColorContrast.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | 3 | extension UIColor { 4 | static let minContrastRatio: CGFloat = 7.0 5 | 6 | /// Calculates the brightness of the receiver 7 | /// Returns nil if the color space of the receiver is not compatible 8 | func brightnessValue() -> CGFloat? { 9 | var h: CGFloat = 0 10 | var s: CGFloat = 0 11 | var b: CGFloat = 0 12 | var a: CGFloat = 0 13 | 14 | if self.getHue(&h, saturation: &s, brightness: &b, alpha: &a) { 15 | return b 16 | } 17 | 18 | return nil 19 | } 20 | 21 | /// Returns an updated version of the receiver with an adjusted brightness component 22 | /// that ensures the receiver and otherColor meet the minContrastRatio 23 | func adjustedColorForBestContrast(withColor otherColor: UIColor) -> UIColor { 24 | guard let contrastRatio = self.contrastRatio(withColor: otherColor), contrastRatio < UIColor.minContrastRatio else { 25 | return self 26 | } 27 | 28 | guard let adjustedBrightness = 29 | self.brightnessToMeetMinContrast(withColor: otherColor) else { 30 | return self 31 | } 32 | return self.adjustedColor(withNewBrightness: adjustedBrightness) 33 | } 34 | 35 | /// Formula for calculating the contrast ratio of two colors is: 36 | /// (b1 + 0.05) / (b2 + 0.05) 37 | /// where b1 and b2 are the brightness values of two colors, and b1 > b2 38 | func contrastRatio(withColor otherColor: UIColor) -> CGFloat? { 39 | guard let b1 = self.brightnessValue(), let b2 = otherColor.brightnessValue() else { 40 | return nil 41 | } 42 | 43 | if b1 > b2 { 44 | return (b1 + 0.05) / (b2 + 0.05) 45 | } else { 46 | return (b2 + 0.05) / (b1 + 0.05) 47 | } 48 | } 49 | 50 | /// Returns the brightness value needed to adjust the receiver color to meet the minContrastRatio 51 | /// when compared to otherColor 52 | func brightnessToMeetMinContrast(withColor otherColor: UIColor) -> CGFloat? { 53 | guard let b1 = self.brightnessValue(), let b2 = otherColor.brightnessValue() else { 54 | return nil 55 | } 56 | 57 | if b1 > b2 { 58 | return UIColor.minContrastRatio * (b2 + 0.05) - 0.05 59 | } else { 60 | return ((b2 + 0.05) / UIColor.minContrastRatio) + 0.05 61 | } 62 | } 63 | 64 | /// Returns a copy of the receiver with the brightness component changed to be 65 | /// the passed in brightness value 66 | func adjustedColor(withNewBrightness brightness: CGFloat) -> UIColor { 67 | var h: CGFloat = 0 68 | var s: CGFloat = 0 69 | var b: CGFloat = 0 70 | var a: CGFloat = 0 71 | if self.getHue(&h, saturation: &s, brightness: &b, alpha: &a) { 72 | return UIColor(hue: h, saturation: s, brightness: brightness, alpha: a) 73 | } 74 | return self; 75 | } 76 | } 77 | 78 | // Test the code! 79 | // color1 represents the foreground color that we will adjust if the contrast ratio is not high enough between color1 and color2 80 | // color2 is the background color that will only be compared to and not changed 81 | 82 | // Check contrast of black on top of white. Adjust the black color if the contrast is < 7 83 | var color1 = UIColor.black 84 | var color2 = UIColor.white 85 | color1.contrastRatio(withColor: color2) 86 | color1.adjustedColorForBestContrast(withColor: color2) // No adjustment needed since contrast is 21, which is > 7 87 | 88 | 89 | // Check contrast of yellow on top of white. Adjust the yellow color if the contrast is < 7 90 | color1 = UIColor.yellow 91 | color2 = UIColor.white 92 | color1.adjustedColorForBestContrast(withColor: color2) // Darken the yellow so the contrast is 7 93 | 94 | 95 | // Check contrast of white on top of white. Adjust the foreground white color if the contrast is < 7 96 | color1 = UIColor.white 97 | color2 = UIColor.white 98 | color1.adjustedColorForBestContrast(withColor: color2) // Darken the foreground white so the contrast is 7 99 | 100 | 101 | // Check contrast of a dark purple on top of black. Adjust the foreground purple color if the contrast is < 7 102 | color1 = UIColor(hue: 0.8, saturation: 1, brightness: 0.15, alpha: 1) 103 | color2 = UIColor.black 104 | color1.adjustedColorForBestContrast(withColor: color2) // Lighten the foreground color so the contrast is 7 105 | -------------------------------------------------------------------------------- /ColorContrast.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ColorContrast.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | 44 | 45 | 49 | 50 | 54 | 55 | 59 | 60 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ColorContrast 2 | UIColor extension in Swift to calculate brightness, color contrast, and more. 3 | 4 | Simply download the ColorContrast Swift Playground and run it! There are colors tests at the bottom of the file to test out the code. 5 | --------------------------------------------------------------------------------