Skip to content

Instantly share code, notes, and snippets.

@warpling
Created September 28, 2020 12:15
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save warpling/f4b9df2f158f17afa0c22822bf0fb28e to your computer and use it in GitHub Desktop.
Save warpling/f4b9df2f158f17afa0c22822bf0fb28e to your computer and use it in GitHub Desktop.
Color Extensions
//
// UIColor+ContrastChecker.swift
// Stay Inside
//
// Created by Ryan McLeod on 5/6/20.
// Copyright © 2020 Grow Pixel. All rights reserved.
//
import UIKit
extension UIColor {
public var shouldUseDarkForeground: Bool {
// Source: https://stackoverflow.com/a/24213274/522498
var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0
guard self.getRed(&red, green: &green, blue: &blue, alpha: nil) else { return true }
return ((0.299 * red) + (0.587 * green) + (0.114 * blue)) > 0.5
}
// W3 based version
// Source: https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_Colors_and_Luminance#Luminance
private var luminosity: Bool {
var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0
guard self.getRed(&red, green: &green, blue: &blue, alpha: nil) else { return true }
if red <= 0.03928 { red = red / 12.92 } else { red = pow((red+0.055)/1.055, 2.4) }
if green <= 0.03928 { green = green / 12.92 } else { green = pow((green+0.055)/1.055, 2.4) }
if blue <= 0.03928 { blue = blue / 12.92 } else { blue = pow((blue+0.055)/1.055, 2.4) }
let lum = (0.2126 * red) + (0.7152 * green) + (0.0722 * blue)
return lum > 0.5
}
}
//
// UIColor+Interpolate.swift
// Stay Inside
//
// Created by Ryan McLeod on 4/21/20.
// Copyright © 2020 Grow Pixel. All rights reserved.
//
import UIKit
extension UIColor {
enum ColorSpace {
case RGB, HSV
}
func interpolate(to: UIColor, by: CGFloat, using colorSpace: ColorSpace = .HSV) -> UIColor {
let by = by.clamped(to: 0...1.0)
guard by > 0 else { return self }
guard by < 1 else { return to }
let start = self.convertToRGB()
let end = to.convertToRGB()
guard (start.cgColor.numberOfComponents == end.cgColor.numberOfComponents),
let startComponents = start.cgColor.components,
let endComponents = end.cgColor.components else {
fatalError("Can't interpolate colors: \(self) -> \(to)")
}
switch colorSpace {
case .RGB:
let (r1, g1, b1, a1) = (startComponents[0], startComponents[1], startComponents[2], startComponents[3])
let (r2, g2, b2, a2) = (endComponents[0], endComponents[1], endComponents[2], endComponents[3])
let (r, g, b, a) = (r1 + (by * (r2 - r1)),
g1 + (by * (g2 - g1)),
b1 + (by * (b2 - b1)),
a1 + (by * (a2 - a1)))
return UIColor(red: r, green: g, blue: b, alpha: a)
case .HSV:
var (h1, s1, b1, a1): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
var (h2, s2, b2, a2): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
start.getHue(&h1, saturation: &s1, brightness: &b1, alpha: &a1)
end.getHue(&h2, saturation: &s2, brightness: &b2, alpha: &a2)
let (h, s, b, a) = (h1 + (by * (h2 - h1)),
s1 + (by * (s2 - s1)),
b1 + (by * (b2 - b1)),
a1 + (by * (a2 - a1)))
return UIColor(hue: h, saturation: s, brightness: b, alpha: a)
}
}
// TODO: Consider renaming to `multiply`
func adjust(hue: CGFloat = 1, saturation: CGFloat = 1, brightness: CGFloat = 1) -> UIColor {
var (h, s, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: (hue * h),
saturation: (saturation * s),
brightness: (brightness * b),
alpha: a)
}
func set(hue: CGFloat? = nil, saturation: CGFloat? = nil, brightness: CGFloat? = nil, alpha: CGFloat? = nil) -> UIColor {
var (h, s, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
getHue(&h, saturation: &s, brightness: &b, alpha: &a)
return UIColor(hue: hue ?? h,
saturation: saturation ?? s,
brightness: brightness ?? b,
alpha: alpha ?? a)
}
// TODO: Consider renaming to `add`
func shift(hue: CGFloat = 0, saturation: CGFloat = 0, brightness: CGFloat = 0) -> UIColor {
var (h, s, b, a): (CGFloat, CGFloat, CGFloat, CGFloat) = (0, 0, 0, 0)
getHue(&h, saturation: &s, brightness: &b, alpha: &a)
// Wraps hue around
return UIColor(hue: (1.0 + h + hue).truncatingRemainder(dividingBy: 1.0),
saturation: (s + saturation).clamped(to: 0.0...1.0),
brightness: (b + brightness).clamped(to: 0.0...1.0),
alpha: a)
}
func convertToRGB() -> UIColor {
switch cgColor.numberOfComponents {
case 2: // Greyscale
let whiteness = cgColor.components![0]
let alpha = cgColor.components![1]
return UIColor(red: whiteness, green: whiteness, blue: whiteness, alpha: alpha)
default:
return self
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment