Skip to content

Commit 34fd22c

Browse files
Merge pull request #69 from componentskit/UKCircularProgress
UKCircularProgress
2 parents bd37630 + 3b428ff commit 34fd22c

File tree

4 files changed

+354
-72
lines changed

4 files changed

+354
-72
lines changed

Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/CircularProgressPreview.swift

+22-9
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,36 @@ struct CircularProgressPreview: View {
66
@State private var model = Self.initialModel
77
@State private var currentValue: CGFloat = Self.initialValue
88

9+
private let circularProgress = UKCircularProgress(
10+
model: Self.initialModel
11+
)
12+
913
private let timer = Timer
1014
.publish(every: 0.5, on: .main, in: .common)
1115
.autoconnect()
12-
16+
1317
var body: some View {
1418
VStack {
19+
PreviewWrapper(title: "UIKit") {
20+
self.circularProgress
21+
.preview
22+
.onAppear {
23+
self.circularProgress.currentValue = Self.initialValue
24+
self.circularProgress.model = Self.initialModel
25+
}
26+
.onChange(of: model) { newModel in
27+
self.circularProgress.model = newModel
28+
}
29+
.onChange(of: self.currentValue) { newValue in
30+
self.circularProgress.currentValue = newValue
31+
}
32+
}
1533
PreviewWrapper(title: "SwiftUI") {
1634
SUCircularProgress(currentValue: self.currentValue, model: self.model)
1735
}
1836
Form {
1937
ComponentColorPicker(selection: self.$model.color)
20-
Picker("Font", selection: self.$model.font) {
21-
Text("Default").tag(Optional<UniversalFont>.none)
22-
Text("Small").tag(UniversalFont.smButton)
23-
Text("Medium").tag(UniversalFont.mdButton)
24-
Text("Large").tag(UniversalFont.lgButton)
25-
Text("Custom: system bold of size 16").tag(UniversalFont.system(size: 16, weight: .bold))
26-
}
38+
CaptionFontPicker(selection: self.$model.font)
2739
Picker("Line Width", selection: self.$model.lineWidth) {
2840
Text("Default").tag(Optional<CGFloat>.none)
2941
Text("2").tag(Optional<CGFloat>.some(2))
@@ -56,8 +68,9 @@ struct CircularProgressPreview: View {
5668
private static var initialValue: Double {
5769
return 0.0
5870
}
71+
5972
private static var initialModel = CircularProgressVM {
60-
$0.label = "0"
73+
$0.label = "0%"
6174
$0.style = .light
6275
}
6376
}

Sources/ComponentsKit/Components/CircularProgress/Models/CircularProgressVM.swift

+54-20
Original file line numberDiff line numberDiff line change
@@ -81,24 +81,24 @@ extension CircularProgressVM {
8181
return .lgCaption
8282
}
8383
}
84+
var stripeWidth: CGFloat {
85+
return 0.5
86+
}
8487
private func stripesCGPath(in rect: CGRect) -> CGMutablePath {
85-
let stripeWidth: CGFloat = 0.5
8688
let stripeSpacing: CGFloat = 3
8789
let stripeAngle: Angle = .degrees(135)
8890

8991
let path = CGMutablePath()
9092
let step = stripeWidth + stripeSpacing
9193
let radians = stripeAngle.radians
92-
let dx = rect.height * tan(radians)
93-
for x in stride(from: dx, through: rect.width + rect.height, by: step) {
94+
95+
let dx: CGFloat = rect.height * tan(radians)
96+
for x in stride(from: 0, through: rect.width + rect.height, by: step) {
9497
let topLeft = CGPoint(x: x, y: 0)
95-
let topRight = CGPoint(x: x + stripeWidth, y: 0)
96-
let bottomLeft = CGPoint(x: x + dx, y: rect.height)
97-
let bottomRight = CGPoint(x: x + stripeWidth + dx, y: rect.height)
98+
let bottomRight = CGPoint(x: x + dx, y: rect.height)
99+
98100
path.move(to: topLeft)
99-
path.addLine(to: topRight)
100101
path.addLine(to: bottomRight)
101-
path.addLine(to: bottomLeft)
102102
path.closeSubpath()
103103
}
104104
return path
@@ -107,37 +107,71 @@ extension CircularProgressVM {
107107

108108
extension CircularProgressVM {
109109
func gap(for normalized: CGFloat) -> CGFloat {
110-
normalized > 0 ? 0.05 : 0
110+
return normalized > 0 ? 0.05 : 0
111111
}
112112

113-
func progressArcStart(for normalized: CGFloat) -> CGFloat {
114-
return 0
115-
}
116-
117-
func progressArcEnd(for normalized: CGFloat) -> CGFloat {
118-
return max(0, min(1, normalized))
119-
}
120-
121-
func backgroundArcStart(for normalized: CGFloat) -> CGFloat {
113+
func stripedArcStart(for normalized: CGFloat) -> CGFloat {
122114
let gapValue = self.gap(for: normalized)
123115
return max(0, min(1, normalized + gapValue))
124116
}
125117

126-
func backgroundArcEnd(for normalized: CGFloat) -> CGFloat {
118+
func stripedArcEnd(for normalized: CGFloat) -> CGFloat {
127119
let gapValue = self.gap(for: normalized)
128120
return 1 - gapValue
129121
}
130122
}
131123

132124
extension CircularProgressVM {
133-
public func progress(for currentValue: CGFloat) -> CGFloat {
125+
func progress(for currentValue: CGFloat) -> CGFloat {
134126
let range = self.maxValue - self.minValue
135127
guard range > 0 else { return 0 }
136128
let normalized = (currentValue - self.minValue) / range
137129
return max(0, min(1, normalized))
138130
}
139131
}
140132

133+
// MARK: - UIKit Helpers
134+
135+
extension CircularProgressVM {
136+
var isStripesLayerHidden: Bool {
137+
switch self.style {
138+
case .light:
139+
return true
140+
case .striped:
141+
return false
142+
}
143+
}
144+
var isBackgroundLayerHidden: Bool {
145+
switch self.style {
146+
case .light:
147+
return false
148+
case .striped:
149+
return true
150+
}
151+
}
152+
func stripesBezierPath(in rect: CGRect) -> UIBezierPath {
153+
let center = CGPoint(x: rect.midX, y: rect.midY)
154+
let path = UIBezierPath(cgPath: self.stripesCGPath(in: rect))
155+
var transform = CGAffineTransform.identity
156+
transform = transform
157+
.translatedBy(x: center.x, y: center.y)
158+
.rotated(by: -CGFloat.pi / 2)
159+
.translatedBy(x: -center.x, y: -center.y)
160+
path.apply(transform)
161+
return path
162+
}
163+
func shouldInvalidateIntrinsicContentSize(_ oldModel: Self) -> Bool {
164+
return self.preferredSize != oldModel.preferredSize
165+
}
166+
func shouldUpdateText(_ oldModel: Self) -> Bool {
167+
return self.label != oldModel.label
168+
}
169+
func shouldRecalculateProgress(_ oldModel: Self) -> Bool {
170+
return self.minValue != oldModel.minValue
171+
|| self.maxValue != oldModel.maxValue
172+
}
173+
}
174+
141175
// MARK: - SwiftUI Helpers
142176

143177
extension CircularProgressVM {

Sources/ComponentsKit/Components/CircularProgress/SUCircularProgress.swift

+24-43
Original file line numberDiff line numberDiff line change
@@ -102,52 +102,33 @@ public struct SUCircularProgress: View {
102102
}
103103

104104
var stripedBackground: some View {
105-
Path { path in
106-
path.addArc(
107-
center: self.model.center,
108-
radius: self.model.radius,
109-
startAngle: .radians(0),
110-
endAngle: .radians(2 * .pi),
111-
clockwise: false
112-
)
113-
}
114-
.trim(
115-
from: self.model.backgroundArcStart(for: self.progress),
116-
to: self.model.backgroundArcEnd(for: self.progress)
117-
)
118-
.stroke(
119-
.clear,
120-
style: StrokeStyle(
121-
lineWidth: self.model.circularLineWidth,
122-
lineCap: .round
105+
StripesShapeCircularProgress(model: self.model)
106+
.stroke(
107+
self.model.color.main.color,
108+
style: StrokeStyle(lineWidth: self.model.stripeWidth)
123109
)
124-
)
125-
.overlay {
126-
StripesShapeCircularProgress(model: self.model)
127-
.foregroundColor(self.model.color.main.color)
128-
.mask {
129-
Path { maskPath in
130-
maskPath.addArc(
131-
center: self.model.center,
132-
radius: self.model.radius,
133-
startAngle: .radians(0),
134-
endAngle: .radians(2 * .pi),
135-
clockwise: false
136-
)
137-
}
138-
.trim(
139-
from: self.model.backgroundArcStart(for: self.progress),
140-
to: self.model.backgroundArcEnd(for: self.progress)
141-
)
142-
.stroke(
143-
style: StrokeStyle(
144-
lineWidth: self.model.circularLineWidth,
145-
lineCap: .round
146-
)
110+
.mask {
111+
Path { maskPath in
112+
maskPath.addArc(
113+
center: self.model.center,
114+
radius: self.model.radius,
115+
startAngle: .radians(0),
116+
endAngle: .radians(2 * .pi),
117+
clockwise: false
147118
)
148119
}
149-
}
150-
.rotationEffect(.degrees(-90))
120+
.trim(
121+
from: self.model.stripedArcStart(for: self.progress),
122+
to: self.model.stripedArcEnd(for: self.progress)
123+
)
124+
.stroke(
125+
style: StrokeStyle(
126+
lineWidth: self.model.circularLineWidth,
127+
lineCap: .round
128+
)
129+
)
130+
}
131+
.rotationEffect(.degrees(-90))
151132
}
152133
}
153134

0 commit comments

Comments
 (0)