-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathRadioButton.swift
167 lines (138 loc) · 5.95 KB
/
RadioButton.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
//
// RadioButton.swift
// RadioCheckboxButton
//
// Created by Manish on 10/01/2018.
// Copyright © 2018 Manish. All rights reserved.
//
import UIKit
// MARK:- RadioButtonDelegate
public protocol RadioButtonDelegate: class {
/// Delegate called when radio button is Selected
///
/// - Parameter button: RadioButton
func radioButtonDidSelect(_ button: RadioButton)
/// Delegate called when radio button is deselected
/// It will call automatically when user choose different radio button than selected
///
/// - Parameter button: RadioButton
func radioButtonDidDeselect(_ button: RadioButton)
}
// MARK:- RadioLayer
internal class RadioLayer: CAShapeLayer {
/// Path for active layer
var activePath: CGPath?
/// Path for inactive layer
var inactivePath: CGPath?
}
// MARK:- RadioCheckboxBaseButton
public class RadioButton: RadioCheckboxBaseButton {
private var outerLayer = CAShapeLayer()
private var innerLayer = RadioLayer()
private var sizeChangeObserver: NSKeyValueObservation?
/// Set your delegate handler
public weak var delegate: RadioButtonDelegate?
/// Apply RadioButtonCircleStyle
public var radioCircle = RadioButtonCircleStyle() {
didSet { setupLayer() }
}
/// Apply RadioButtonColor
public var radioButtonColor: RadioButtonColor! {
didSet {
innerLayer.fillColor = radioButtonColor.active.cgColor
outerLayer.strokeColor = isOn ? radioButtonColor.active.cgColor : radioButtonColor.inactive.cgColor
}
}
/// Don't allow deselectio of Radio button as per standart radio button feature
override internal var allowDeselection: Bool {
return false
}
/// Do initial stuff here
/// Setting default color style
override internal func setup() {
radioButtonColor = RadioButtonColor(active: tintColor, inactive: UIColor.lightGray)
style = .circle
super.setup()
}
/// Create layer for Radio button
override internal func setupLayer() {
contentEdgeInsets = UIEdgeInsets(top: 0, left: radioCircle.outer + radioCircle.contentPadding, bottom: 0, right: 0)
// Add layer here
func addOuterLayer() {
outerLayer.strokeColor = radioButtonColor.active.cgColor
outerLayer.fillColor = UIColor.clear.cgColor
outerLayer.lineWidth = radioCircle.lineWidth
outerLayer.path = UIBezierPath.outerCircle(rect: bounds, circle: radioCircle, style: style).cgPath
outerLayer.removeFromSuperlayer()
layer.insertSublayer(outerLayer, at: 0)
}
func addInnerLayer() {
guard let rect = outerLayer.path?.boundingBox else { return }
innerLayer.fillColor = radioButtonColor.active.cgColor
innerLayer.strokeColor = UIColor.clear.cgColor
innerLayer.lineWidth = 0
innerLayer.activePath = UIBezierPath.innerCircleActive(rect: rect, circle: radioCircle, style: style).cgPath
innerLayer.inactivePath = UIBezierPath.innerCircleInactive(rect: rect).cgPath
innerLayer.path = innerLayer.inactivePath
innerLayer.removeFromSuperlayer()
outerLayer.insertSublayer(innerLayer, at: 0)
}
addOuterLayer()
addInnerLayer()
super.setupLayer()
}
/// Call to delegate
override internal func callDelegate() {
if isOn {
delegate?.radioButtonDidSelect(self)
} else {
delegate?.radioButtonDidDeselect(self)
}
}
/// Updating active layers
override internal func updateActiveLayer() {
super.updateActiveLayer()
outerLayer.strokeColor = radioButtonColor.active.cgColor
guard let start = innerLayer.path, let end = innerLayer.activePath else { return }
innerLayer.animatePath(start: start, end: end)
innerLayer.path = end
}
/// Updating inactive layers
override internal func updateInactiveLayer() {
super.updateInactiveLayer()
outerLayer.strokeColor = radioButtonColor.inactive.cgColor
guard let start = innerLayer.path, let end = innerLayer.inactivePath else { return }
innerLayer.animatePath(start: start, end: end)
innerLayer.path = end
}
}
// MARK:- Radio button layer path
private extension UIBezierPath {
/// Get outer circle layer
static func outerCircle(rect: CGRect, circle: RadioButtonCircleStyle, style: RadioCheckboxStyle) -> UIBezierPath {
let size = CGSize(width: circle.outer, height: circle.outer)
let newRect = CGRect(origin: CGPoint(x: circle.lineWidth/2, y: rect.size.height/2-(circle.outer/2)), size: size)
switch style {
case .circle: return UIBezierPath(roundedRect: newRect, cornerRadius: size.height/2)
case .square: return UIBezierPath(rect: newRect)
case .rounded(let radius): return UIBezierPath(roundedRect: newRect, cornerRadius: radius)
}
}
/// Get inner circle layer
static func innerCircleActive(rect: CGRect, circle: RadioButtonCircleStyle, style: RadioCheckboxStyle) -> UIBezierPath {
let size = CGSize(width: circle.inner, height: circle.inner)
let origon = CGPoint(x: rect.midX-size.width/2, y: rect.midY-size.height/2)
let newRect = CGRect(origin: origon, size: size)
switch style {
case .circle: return UIBezierPath(roundedRect: newRect, cornerRadius: size.height/2)
case .square: return UIBezierPath(rect: newRect)
case .rounded(let radius): return UIBezierPath(roundedRect: newRect, cornerRadius: radius)
}
}
/// Get inner circle layer for inactive state
static func innerCircleInactive(rect: CGRect) -> UIBezierPath {
let origin = CGPoint(x: rect.midX, y: rect.midY)
let frame = CGRect(origin: origin, size: CGSize.zero)
return UIBezierPath(rect: frame)
}
}