-
Notifications
You must be signed in to change notification settings - Fork 41
/
InitialsImageView.swift
162 lines (123 loc) · 7.19 KB
/
InitialsImageView.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//
// InitialsImageView.swift
//
//
// Created by Tom Bachant on 1/28/17.
//
//
import UIKit
let kFontResizingProportion: CGFloat = 0.4
let kColorMinComponent: Int = 30
let kColorMaxComponent: Int = 214
public typealias GradientColors = (top: UIColor, bottom: UIColor)
typealias HSVOffset = (hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat)
let kGradientTopOffset: HSVOffset = (hue: -0.025, saturation: 0.05, brightness: 0, alpha: 0)
let kGradientBotomOffset: HSVOffset = (hue: 0.025, saturation: -0.05, brightness: 0, alpha: 0)
extension UIImageView {
public func setImageForName(_ string: String, backgroundColor: UIColor? = nil, circular: Bool, textAttributes: [NSAttributedString.Key: AnyObject]?, gradient: Bool = false) {
setImageForName(string, backgroundColor: backgroundColor, circular: circular, textAttributes: textAttributes, gradient: gradient, gradientColors: nil)
}
public func setImageForName(_ string: String, gradientColors: GradientColors, circular: Bool, textAttributes: [NSAttributedString.Key: AnyObject]?) {
setImageForName(string, backgroundColor: nil, circular: circular, textAttributes: textAttributes, gradient: true, gradientColors: gradientColors)
}
private func setImageForName(_ string: String, backgroundColor: UIColor?, circular: Bool, textAttributes: [NSAttributedString.Key: AnyObject]?, gradient: Bool = false, gradientColors: GradientColors?) {
let initials: String = initialsFromString(string: string)
let color: UIColor = (backgroundColor != nil) ? backgroundColor! : randomColor(for: string)
let gradientColors = gradientColors ?? topAndBottomColors(for: color)
let attributes: [NSAttributedString.Key: AnyObject] = (textAttributes != nil) ? textAttributes! : [
NSAttributedString.Key.font: self.fontForFontName(name: nil),
NSAttributedString.Key.foregroundColor: UIColor.white
]
self.image = imageSnapshot(text: initials, backgroundColor: color, circular: circular, textAttributes: attributes, gradient: gradient, gradientColors: gradientColors)
}
private func fontForFontName(name: String?) -> UIFont {
let fontSize = self.bounds.width * kFontResizingProportion
guard let name = name else { return .systemFont(ofSize: fontSize) }
guard let customFont = UIFont(name: name, size: fontSize) else { return .systemFont(ofSize: fontSize) }
return customFont
}
private func imageSnapshot(text imageText: String, backgroundColor: UIColor, circular: Bool, textAttributes: [NSAttributedString.Key : AnyObject], gradient: Bool, gradientColors: GradientColors) -> UIImage {
let scale: CGFloat = UIScreen.main.scale
var size: CGSize = self.bounds.size
if (self.contentMode == .scaleToFill ||
self.contentMode == .scaleAspectFill ||
self.contentMode == .scaleAspectFit ||
self.contentMode == .redraw) {
size.width = (size.width * scale) / scale
size.height = (size.height * scale) / scale
}
UIGraphicsBeginImageContextWithOptions(size, false, scale)
guard let context: CGContext = UIGraphicsGetCurrentContext() else { return UIImage() }
if circular {
// Clip context to a circle
let path: CGPath = CGPath(ellipseIn: self.bounds, transform: nil)
context.addPath(path)
context.clip()
}
if gradient {
// Draw a gradient from the top to the bottom
let baseSpace = CGColorSpaceCreateDeviceRGB()
let colors = [gradientColors.top.cgColor, gradientColors.bottom.cgColor]
if let gradient = CGGradient(colorsSpace: baseSpace, colors: colors as CFArray, locations: nil) {
let startPoint = CGPoint(x: self.bounds.midX, y: self.bounds.minY)
let endPoint = CGPoint(x: self.bounds.midX, y: self.bounds.maxY)
context.drawLinearGradient(gradient, start: startPoint, end: endPoint, options: CGGradientDrawingOptions(rawValue: 0))
}
} else {
// Fill background of context
context.setFillColor(backgroundColor.cgColor)
context.fill(CGRect(x: 0, y: 0, width: size.width, height: size.height))
}
// Draw text in the context
let textSize: CGSize = imageText.size(withAttributes: textAttributes)
let bounds: CGRect = self.bounds
imageText.draw(in: CGRect(x: bounds.midX - textSize.width / 2,
y: bounds.midY - textSize.height / 2,
width: textSize.width,
height: textSize.height),
withAttributes: textAttributes)
guard let snapshot: UIImage = UIGraphicsGetImageFromCurrentImageContext() else { return UIImage() }
UIGraphicsEndImageContext()
return snapshot
}
}
private func initialsFromString(string: String) -> String {
var nameComponents = string.uppercased().components(separatedBy: CharacterSet.letters.inverted)
nameComponents.removeAll(where: {$0.isEmpty})
let firstInitial = nameComponents.first?.first
let lastInitial = nameComponents.count > 1 ? nameComponents.last?.first : nil
return (firstInitial != nil ? "\(firstInitial!)" : "") + (lastInitial != nil ? "\(lastInitial!)" : "")
}
private func randomColorComponent() -> Int {
let limit = kColorMaxComponent - kColorMinComponent
return kColorMinComponent + Int(drand48() * Double(limit))
}
private func randomColor(for string: String) -> UIColor {
srand48(string.hashValue)
let red = CGFloat(randomColorComponent()) / 255.0
let green = CGFloat(randomColorComponent()) / 255.0
let blue = CGFloat(randomColorComponent()) / 255.0
return UIColor(red: red, green: green, blue: blue, alpha: 1.0)
}
private func clampColorComponent(_ value: CGFloat) -> CGFloat {
return min(max(value, 0), 1)
}
private func correctColorComponents(of color: UIColor, withHSVOffset offset: HSVOffset) -> UIColor {
var hue = CGFloat(0)
var saturation = CGFloat(0)
var brightness = CGFloat(0)
var alpha = CGFloat(0)
if color.getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
hue = clampColorComponent(hue + offset.hue)
saturation = clampColorComponent(saturation + offset.saturation)
brightness = clampColorComponent(brightness + offset.brightness)
alpha = clampColorComponent(alpha + offset.alpha)
return UIColor(hue: hue, saturation: saturation, brightness: brightness, alpha: alpha)
}
return color
}
private func topAndBottomColors(for color: UIColor, withTopHSVOffset topHSVOffset: HSVOffset = kGradientTopOffset, withBottomHSVOffset bottomHSVOffset: HSVOffset = kGradientBotomOffset) -> GradientColors {
let topColor = correctColorComponents(of: color, withHSVOffset: topHSVOffset)
let bottomColor = correctColorComponents(of: color, withHSVOffset: bottomHSVOffset)
return (top: topColor, bottom: bottomColor)
}