UIColor的一些扩展
1 前言
在阅读Telegram
源码的过程中,抽出业务代码,留下有用的代码,同时也修改或总结一些自己遇到的好用的方法.
2 颜色
红绿蓝三原色 (RGB)
发光的媒体(比如电视机)使用红、绿 和蓝加色的三元色,每种光尽可能只刺激针对它们的锥状细胞而不刺激其它的锥状细胞。这个系统的色域占人可以感受到的色彩空间的大部分,因此电视机和电脑萤幕使用这个系统。
理论上我们也可以使用其他颜色作为元色,但使用红、绿和蓝我们可以最大地达到人的色彩空间。遗憾的是对于红、绿和蓝色没有固定的波长的定义,因此不同的技术仪器可能使用不同的波长从而在萤幕上产生稍微不同的颜色。
色相、色度和明度系统 (HSB)
在制作计算机图像时人们往往使用另一种颜色系统。这个颜色系统使用三项分类,分别叫做色相(hue)、色度(saturation)和明度(brightness)的系数。色调决定到底哪一种颜色被使用,色度决定颜色的纯度,亮度决定颜色的明暗程度
参考wiki
3 UIColor
初始化扩展
rgb
argb
hex
数值或者字符串颜色值的初始化
convenience init(rgb: UInt64) {
self.init(red: CGFloat((rgb >> 16) & 0xff) / 255.0, green: CGFloat((rgb >> 8) & 0xff) / 255.0, blue: CGFloat(rgb & 0xff) / 255.0, alpha: 1.0)
}
convenience init(rgb: UInt64, alpha: CGFloat) {
self.init(red: CGFloat((rgb >> 16) & 0xff) / 255.0, green: CGFloat((rgb >> 8) & 0xff) / 255.0, blue: CGFloat(rgb & 0xff) / 255.0, alpha: alpha)
}
convenience init(argb: UInt64) {
self.init(red: CGFloat((argb >> 16) & 0xff) / 255.0, green: CGFloat((argb >> 8) & 0xff) / 255.0, blue: CGFloat(argb & 0xff) / 255.0, alpha: CGFloat((argb >> 24) & 0xff) / 255.0)
}
convenience init?(hex: String) {
let scanner = Scanner(string: hex)
if hex.hasPrefix("#") {
scanner.scanLocation = 1
}
var value: UInt64 = 0
if scanner.scanHexInt64(&value) {
if hex.count > 7 {
self.init(argb: value)
} else {
self.init(rgb: value)
}
} else {
return nil
}
}
<<
,右移:>>
对一个数进行按位左移或按位右移,相当于对这个数进行乘以 2 或除以 2 的运算。将一个整数左移一位,等价于将这个数乘以 2,同样地,将一个整数右移一位,等价于将这个数除以 2。
十六进制一个字符4位,例如 #AABBCC AA代表红,BB代表绿,CC代表蓝,三个值分别都以255为基数,要得到AA的值,可以按位与
&
0xFF0000 取反再右移16位的到0xAA即十进制170.4 属性扩展
4.1 alpha透明度
var alpha: CGFloat {
var alpha: CGFloat = 0.0
if self.getRed(nil, green: nil, blue: nil, alpha: &alpha) {
return alpha
} else if self.getWhite(nil, alpha: &alpha) {
return alpha
} else {
return 0.0
}
}
4.2 RGB值
var rgb: UInt64 {
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
if self.getRed(&red, green: &green, blue: &blue, alpha: nil) {
return (UInt64(max(0.0, red) * 255.0) << 16) | (UInt64(max(0.0, green) * 255.0) << 8) | (UInt64(max(0.0, blue) * 255.0))
} else if self.getWhite(&red, alpha: nil) {
return (UInt64(max(0.0, red) * 255.0) << 16) | (UInt64(max(0.0, red) * 255.0) << 8) | (UInt64(max(0.0, red) * 255.0))
} else {
return 0
}
}
4.3 ARGB值
var argb: UInt64 {
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
var alpha: CGFloat = 0.0
if self.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
return (UInt64(alpha * 255.0) << 24) | (UInt64(max(0.0, red) * 255.0) << 16) | (UInt64(max(0.0, green) * 255.0) << 8) | (UInt64(max(0.0, blue) * 255.0))
} else if self.getWhite(&red, alpha: &alpha) {
return (UInt64(max(0.0, alpha) * 255.0) << 24) | (UInt64(max(0.0, red) * 255.0) << 16) | (UInt64(max(0.0, red) * 255.0) << 8) | (UInt64(max(0.0, red) * 255.0))
} else {
return 0
}
}
4.4 HSB值
var hsb: (h: CGFloat, s: CGFloat, b: CGFloat) {
var hue: CGFloat = 0.0
var saturation: CGFloat = 0.0
var brightness: CGFloat = 0.0
if self.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: nil) {
return (hue, saturation, brightness)
} else {
return (0.0, 0.0, 0.0)
}
}
4.5 明度值
var lightness: CGFloat {
var red: CGFloat = 0.0
var green: CGFloat = 0.0
var blue: CGFloat = 0.0
if self.getRed(&red, green: &green, blue: &blue, alpha: nil) {
return 0.2126 * red + 0.7152 * green + 0.0722 * blue
} else if self.getWhite(&red, alpha: nil) {
return red
} else {
return 0.0
}
}
4.6 HEX字符串
var hexString: String {
return String(rgb, radix: 16, uppercase: false).rightJustified(width: 6, pad: "0")
}
rightJustified
extension String {
func rightJustified(width: Int, pad: String = " ", truncate: Bool = false) -> String {
guard width > count else {
return truncate ? String(suffix(width)) : self
}
return String(repeating: pad, count: width - count) + self
}
}
5 方法扩展
5.1 改变亮度明度(根据传入系数使亮度明度乘积得到新颜色)
func withMultipliedBrightnessBy(_ factor: CGFloat) -> UIColor {
var hue: CGFloat = 0.0
var saturation: CGFloat = 0.0
var brightness: CGFloat = 0.0
var alpha: CGFloat = 0.0
self.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha)
return UIColor(hue: hue, saturation: saturation, brightness: max(0.0, min(1.0, brightness * factor)), alpha: alpha)
}
如下调整.red
红色亮度比例从左到右分别为0.3,0.6,1(不改变)
override func viewDidLoad() {
super.viewDidLoad()
let bgView1 = UIView(frame: CGRectMake(17, 100, 100, 100))
bgView1.backgroundColor = .red.withMultipliedBrightnessBy(0.3)
self.view.addSubview(bgView1)
let bgView2 = UIView(frame: CGRectMake(127, 100, 100, 100))
bgView2.backgroundColor = .red.withMultipliedBrightnessBy(0.6)
self.view.addSubview(bgView2)
let bgView3 = UIView(frame: CGRectMake(237, 100, 100, 100))
bgView3.backgroundColor = .red.withMultipliedBrightnessBy(1)
self.view.addSubview(bgView3)
}
5.2 改变HSB值
func withMultiplied(hue: CGFloat, saturation: CGFloat, brightness: CGFloat) -> UIColor {
var hueValue: CGFloat = 0.0
var saturationValue: CGFloat = 0.0
var brightnessValue: CGFloat = 0.0
var alphaValue: CGFloat = 0.0
self.getHue(&hueValue, saturation: &saturationValue, brightness: &brightnessValue, alpha: &alphaValue)
return UIColor(hue: max(0.0, min(1.0, hueValue * hue)), saturation: max(0.0, min(1.0, saturationValue * saturation)), brightness: max(0.0, min(1.0, brightnessValue * brightness)), alpha: alphaValue)
}
如下调整.red
颜色HSB值
private func setupViews2() {
let bgView1 = UIView(frame: CGRectMake(17, 100, 100, 100))
bgView1.backgroundColor = .red.withMultiplied(hue: 1.034, saturation: 0.819, brightness: 0.633)
self.view.addSubview(bgView1)
let bgView2 = UIView(frame: CGRectMake(127, 100, 100, 100))
bgView2.backgroundColor = .red.withMultiplied(hue: 1.029, saturation: 0.77, brightness: 0.332)
self.view.addSubview(bgView2)
let bgView3 = UIView(frame: CGRectMake(237, 100, 100, 100))
bgView3.backgroundColor = .red.withMultiplied(hue: 1.034, saturation: 0.583, brightness: 1.234)
self.view.addSubview(bgView3)
}
5.3 混合颜色(alpha合成)
RGB混合算法目前在常用到的算法是AlphaBlend
.
计算公式如下:假设一幅图象是A,另一幅透明的图象是B,那么透过B去看A,看上去的图象C就是B和A的混合图象,设B图象的透明度为alpha(取值为0-1,1为完全透明,0为完全不透明).
Alpha
混合公式如下:
R(C)=(1-alpha)*R(B) + alpha*R(A)
G(C)=(1-alpha)*G(B) + alpha*G(A)
B(C)=(1-alpha)*B(B) + alpha*B(A)
R(x)、G(x)、B(x)分别指颜色x的RGB分量原色值。从上面的公式可以知道,Alpha其实是一个决定混合透明度的数值。 这里只对B图的Alpha进行了处理,但A图本身如果也有透明通道的,也需要进行一样的处理,即
A(C)=(1-alpha)*A(B) + alpha*A(A)
func mixedWith(_ other: UIColor, alpha: CGFloat) -> UIColor {
let alpha = min(1.0, max(0.0, alpha))
let oneMinusAlpha = 1.0 - alpha
var r1: CGFloat = 0.0
var r2: CGFloat = 0.0
var g1: CGFloat = 0.0
var g2: CGFloat = 0.0
var b1: CGFloat = 0.0
var b2: CGFloat = 0.0
var a1: CGFloat = 0.0
var a2: CGFloat = 0.0
if self.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) &&
other.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
{
let r = r1 * oneMinusAlpha + r2 * alpha
let g = g1 * oneMinusAlpha + g2 * alpha
let b = b1 * oneMinusAlpha + b2 * alpha
let a = a1 * oneMinusAlpha + a2 * alpha
return UIColor(red: r, green: g, blue: b, alpha: a)
}
return self
}
示例如下将0.5透明度的黄色覆盖到红色上面,透过上面的黄色看下面的红色,得到的颜色:
private func setupViews3() {
let bgView1 = UIView(frame: CGRectMake(17, 100, 100, 100))
bgView1.backgroundColor = .red
self.view.addSubview(bgView1)
let bgView2 = UIView(frame: CGRectMake(127, 100, 100, 100))
bgView2.backgroundColor = .yellow
self.view.addSubview(bgView2)
let bgView3 = UIView(frame: CGRectMake(237, 100, 100, 100))
bgView3.backgroundColor = .red.mixedWith(.yellow, alpha: 0.5)
self.view.addSubview(bgView3)
}
blitOver
和上面的mixedWith
方法相反,表示覆盖到另一个颜色上面作为前景色.
func blitOver(_ other: UIColor, alpha: CGFloat) -> UIColor {
let alpha = min(1.0, max(0.0, alpha))
var r1: CGFloat = 0.0
var r2: CGFloat = 0.0
var g1: CGFloat = 0.0
var g2: CGFloat = 0.0
var b1: CGFloat = 0.0
var b2: CGFloat = 0.0
var a1: CGFloat = 0.0
var a2: CGFloat = 0.0
if self.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) &&
other.getRed(&r2, green: &g2, blue: &b2, alpha: &a2)
{
let resultingAlpha = max(0.0, min(1.0, alpha * a1))
let oneMinusResultingAlpha = 1.0 - resultingAlpha
let r = r1 * resultingAlpha + r2 * oneMinusResultingAlpha
let g = g1 * resultingAlpha + g2 * oneMinusResultingAlpha
let b = b1 * resultingAlpha + b2 * oneMinusResultingAlpha
let a: CGFloat = 1.0
return UIColor(red: r, green: g, blue: b, alpha: a)
}
return self
}
5.4 调整透明度倍数
func withMultipliedAlpha(_ alpha: CGFloat) -> UIColor {
var r1: CGFloat = 0.0
var g1: CGFloat = 0.0
var b1: CGFloat = 0.0
var a1: CGFloat = 0.0
if self.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) {
return UIColor(red: r1, green: g1, blue: b1, alpha: max(0.0, min(1.0, a1 * alpha)))
}
return self
}
5.5 篡改一个颜色到另一个颜色
fraction
取值范围为0~1,当为0时,返回当前颜色,当为1时,返回传入颜色. 从0到1的变化既是当前颜色到传入颜色的变化.
func interpolateTo(_ color: UIColor, fraction: CGFloat) -> UIColor? {
let f = min(max(0, fraction), 1)
var r1: CGFloat = 0.0
var r2: CGFloat = 0.0
var g1: CGFloat = 0.0
var g2: CGFloat = 0.0
var b1: CGFloat = 0.0
var b2: CGFloat = 0.0
var a1: CGFloat = 0.0
var a2: CGFloat = 0.0
if self.getRed(&r1, green: &g1, blue: &b1, alpha: &a1) &&
color.getRed(&r2, green: &g2, blue: &b2, alpha: &a2) {
let r: CGFloat = CGFloat(r1 + (r2 - r1) * f)
let g: CGFloat = CGFloat(g1 + (g2 - g1) * f)
let b: CGFloat = CGFloat(b1 + (b2 - b1) * f)
let a: CGFloat = CGFloat(a1 + (a2 - a1) * f)
return UIColor(red: r, green: g, blue: b, alpha: a)
} else {
return self
}
}
5.6 欧氏距离
private var colorComponents: (r: Int64, g: Int64, b: Int64) {
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
if self.getRed(&r, green: &g, blue: &b, alpha: nil) {
return (Int64(max(0.0, r) * 255.0), Int64(max(0.0, g) * 255.0), Int64(max(0.0, b) * 255.0))
} else if self.getWhite(&r, alpha: nil) {
return (Int64(max(0.0, r) * 255.0), Int64(max(0.0, r) * 255.0), Int64(max(0.0, r) * 255.0))
}
return (0, 0, 0)
}
func distance(to other: UIColor) -> Int64 {
let e1 = self.colorComponents
let e2 = other.colorComponents
let rMean = (e1.r + e2.r) / 2
let r = e1.r - e2.r
let g = e1.g - e2.g
let b = e1.b - e2.b
let a1 = ((512 + rMean) * r * r) >> 8
let b1 = 4 * g * g
let c1 = ((767 - rMean) * b * b) >> 8
return a1 + b1 + c1
}
5.7 平均颜色
static func average(of colors: [UIColor]) -> UIColor {
var sr: CGFloat = 0.0
var sg: CGFloat = 0.0
var sb: CGFloat = 0.0
var sa: CGFloat = 0.0
for color in colors {
var r: CGFloat = 0.0
var g: CGFloat = 0.0
var b: CGFloat = 0.0
var a: CGFloat = 0.0
color.getRed(&r, green: &g, blue: &b, alpha: &a)
sr += r
sg += g
sb += b
sa += a
}
return UIColor(red: sr / CGFloat(colors.count), green: sg / CGFloat(colors.count), blue: sb / CGFloat(colors.count), alpha: sa / CGFloat(colors.count))
}