-
Notifications
You must be signed in to change notification settings - Fork 107
/
BadgeSwift.swift
188 lines (150 loc) · 4.93 KB
/
BadgeSwift.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
import UIKit
/**
Badge view control for iOS and tvOS.
Project home: https://github.com/evgenyneu/swift-badge
*/
@IBDesignable open class BadgeSwift: UILabel {
/// Background color of the badge
@IBInspectable open var badgeColor: UIColor = UIColor.red {
didSet {
setNeedsDisplay()
}
}
/// Width of the badge border
@IBInspectable open var borderWidth: CGFloat = 0 {
didSet {
invalidateIntrinsicContentSize()
}
}
/// Color of the bardge border
@IBInspectable open var borderColor: UIColor = UIColor.white {
didSet {
invalidateIntrinsicContentSize()
}
}
/// Badge insets that describe the margin between text and the edge of the badge.
@IBInspectable open var insets: CGSize = CGSize(width: 5, height: 2) {
didSet {
invalidateIntrinsicContentSize()
}
}
// MARK: Badge shadow
// --------------------------
/// Opacity of the badge shadow
@IBInspectable open var shadowOpacityBadge: CGFloat = 0.5 {
didSet {
layer.shadowOpacity = Float(shadowOpacityBadge)
setNeedsDisplay()
}
}
/// Size of the badge shadow
@IBInspectable open var shadowRadiusBadge: CGFloat = 0.5 {
didSet {
layer.shadowRadius = shadowRadiusBadge
setNeedsDisplay()
}
}
/// Color of the badge shadow
@IBInspectable open var shadowColorBadge: UIColor = UIColor.black {
didSet {
layer.shadowColor = shadowColorBadge.cgColor
setNeedsDisplay()
}
}
/// Offset of the badge shadow
@IBInspectable open var shadowOffsetBadge: CGSize = CGSize(width: 0, height: 0) {
didSet {
layer.shadowOffset = shadowOffsetBadge
setNeedsDisplay()
}
}
/// Corner radius of the badge. -1 if unspecified. When unspecified, the corner is fully rounded. Default: -1.
@IBInspectable open var cornerRadius: CGFloat = -1 {
didSet {
setNeedsDisplay()
}
}
/// Initialize the badge view
convenience public init() {
self.init(frame: CGRect())
}
/// Initialize the badge view
override public init(frame: CGRect) {
super.init(frame: frame)
setup()
}
/// Initialize the badge view
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
/// Add custom insets around the text
override open func textRect(forBounds bounds: CGRect, limitedToNumberOfLines numberOfLines: Int) -> CGRect {
let rect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
var insetsWithBorder = actualInsetsWithBorder()
let rectWithDefaultInsets = rect.insetBy(dx: -insetsWithBorder.width, dy: -insetsWithBorder.height)
// If width is less than height
// Adjust the width insets to make it look round
if rectWithDefaultInsets.width < rectWithDefaultInsets.height {
insetsWithBorder.width = (rectWithDefaultInsets.height - rect.width) / 2
}
let result = rect.insetBy(dx: -insetsWithBorder.width, dy: -insetsWithBorder.height)
return result
}
/// Draws the label with insets
override open func drawText(in rect: CGRect) {
if cornerRadius >= 0 {
layer.cornerRadius = cornerRadius
}else {
// Use fully rounded corner if radius is not specified
layer.cornerRadius = rect.height / 2
}
let insetsWithBorder = actualInsetsWithBorder()
let insets = UIEdgeInsets(
top: insetsWithBorder.height,
left: insetsWithBorder.width,
bottom: insetsWithBorder.height,
right: insetsWithBorder.width)
let rectWithoutInsets = rect.inset(by: insets)
super.drawText(in: rectWithoutInsets)
}
/// Draw the background of the badge
override open func draw(_ rect: CGRect) {
let rectInset = rect.insetBy(dx: borderWidth/2, dy: borderWidth/2)
let actualCornerRadius = cornerRadius >= 0 ? cornerRadius : rect.height/2
var path: UIBezierPath?
if actualCornerRadius == 0 {
// Use rectangular path when corner radius is zero as a workaround
// a glith in the left top corner with UIBezierPath(roundedRect).
path = UIBezierPath(rect: rectInset)
} else {
path = UIBezierPath(roundedRect: rectInset, cornerRadius: actualCornerRadius)
}
badgeColor.setFill()
path?.fill()
if borderWidth > 0 {
borderColor.setStroke()
path?.lineWidth = borderWidth
path?.stroke()
}
super.draw(rect)
}
private func setup() {
textAlignment = NSTextAlignment.center
clipsToBounds = false // Allows shadow to spread beyond the bounds of the badge
}
/// Size of the insets plus the border
private func actualInsetsWithBorder() -> CGSize {
return CGSize(
width: insets.width + borderWidth,
height: insets.height + borderWidth
)
}
/// Draw the stars in interface builder
@available(iOS 8.0, *)
override open func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setup()
setNeedsDisplay()
}
}