Skip to content

Commit 035e486

Browse files
Merge pull request #50 from componentskit/UKProgressBar
UKProgressBar
2 parents d6d9e83 + 06e613b commit 035e486

File tree

5 files changed

+316
-47
lines changed

5 files changed

+316
-47
lines changed

Examples/DemosApp/DemosApp/ComponentsPreview/PreviewPages/ProgressBarPreview.swift

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,28 @@ import SwiftUI
33
import UIKit
44

55
struct ProgressBarPreview: View {
6-
@State private var model = ProgressBarVM()
7-
@State private var currentValue: CGFloat = 0
6+
@State private var model = Self.initialModel
7+
@State private var currentValue: CGFloat = Self.initialValue
8+
9+
private let progressBar = UKProgressBar(initialValue: Self.initialValue, model: Self.initialModel)
10+
811
private let timer = Timer
912
.publish(every: 0.1, on: .main, in: .common)
1013
.autoconnect()
1114

1215
var body: some View {
1316
VStack {
17+
PreviewWrapper(title: "UIKit") {
18+
self.progressBar
19+
.preview
20+
.onAppear {
21+
self.progressBar.currentValue = self.currentValue
22+
self.progressBar.model = Self.initialModel
23+
}
24+
.onChange(of: self.model) { newValue in
25+
self.progressBar.model = newValue
26+
}
27+
}
1428
PreviewWrapper(title: "SwiftUI") {
1529
SUProgressBar(currentValue: self.$currentValue, model: self.model)
1630
}
@@ -33,8 +47,19 @@ struct ProgressBarPreview: View {
3347
} else {
3448
self.currentValue = self.model.minValue
3549
}
50+
51+
self.progressBar.currentValue = self.currentValue
3652
}
3753
}
54+
55+
// MARK: - Helpers
56+
57+
private static var initialValue: Double {
58+
return 0.0
59+
}
60+
private static var initialModel: ProgressBarVM {
61+
return .init()
62+
}
3863
}
3964

4065
#Preview {

Sources/ComponentsKit/ProgressBar/Models/ProgressBarStyle.swift renamed to Sources/ComponentsKit/Components/ProgressBar/Models/ProgressBarStyle.swift

File renamed without changes.

Sources/ComponentsKit/ProgressBar/Models/ProgressBarVM.swift renamed to Sources/ComponentsKit/Components/ProgressBar/Models/ProgressBarVM.swift

Lines changed: 51 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,7 @@ public struct ProgressBarVM: ComponentVM {
3535
// MARK: - Shared Helpers
3636

3737
extension ProgressBarVM {
38-
var innerBarPadding: CGFloat {
39-
return 3
40-
}
41-
42-
var barHeight: CGFloat {
38+
var backgroundHeight: CGFloat {
4339
switch self.style {
4440
case .light:
4541
switch size {
@@ -62,25 +58,42 @@ extension ProgressBarVM {
6258
}
6359
}
6460

65-
var computedCornerRadius: CGFloat {
61+
var progressHeight: CGFloat {
62+
return self.backgroundHeight - self.progressPadding
63+
}
64+
65+
func cornerRadius(for height: CGFloat) -> CGFloat {
6666
switch self.cornerRadius {
6767
case .none:
68-
return 0.0
68+
return 0
6969
case .small:
70-
return self.barHeight / 3.5
70+
return height / 3.5
7171
case .medium:
72-
return self.barHeight / 3.0
72+
return height / 3.0
7373
case .large:
74-
return self.barHeight / 2.5
74+
return height / 2.5
7575
case .full:
76-
return self.barHeight / 2.0
76+
return height / 2.0
7777
case .custom(let value):
78-
return min(value, self.barHeight / 2)
78+
return min(value, height / 2)
7979
}
8080
}
8181

82-
var innerCornerRadius: CGFloat {
83-
return max(0, self.computedCornerRadius - self.innerBarPadding)
82+
var animationDuration: TimeInterval {
83+
return 0.2
84+
}
85+
86+
var progressPadding: CGFloat {
87+
switch self.style {
88+
case .light:
89+
return 0
90+
case .filled, .striped:
91+
return 3
92+
}
93+
}
94+
95+
var lightBarSpacing: CGFloat {
96+
return 4
8497
}
8598

8699
var backgroundColor: UniversalColor {
@@ -101,10 +114,6 @@ extension ProgressBarVM {
101114
}
102115
}
103116

104-
func shouldUpdateLayout(_ oldModel: Self) -> Bool {
105-
return self.size != oldModel.size
106-
}
107-
108117
private func stripesCGPath(in rect: CGRect) -> CGMutablePath {
109118
let stripeWidth: CGFloat = 2
110119
let stripeSpacing: CGFloat = 4
@@ -127,14 +136,35 @@ extension ProgressBarVM {
127136
}
128137
return path
129138
}
139+
}
130140

131-
public func stripesPath(in rect: CGRect) -> Path {
132-
return Path(self.stripesCGPath(in: rect))
141+
extension ProgressBarVM {
142+
func progress(for currentValue: CGFloat) -> CGFloat {
143+
let range = self.maxValue - self.minValue
144+
guard range > 0 else { return 0 }
145+
let normalized = (currentValue - self.minValue) / range
146+
return max(0, min(1, normalized))
133147
}
148+
}
134149

135-
public func stripesBezierPath(in rect: CGRect) -> UIBezierPath {
150+
// MARK: - UIKit Helpers
151+
152+
extension ProgressBarVM {
153+
func stripesBezierPath(in rect: CGRect) -> UIBezierPath {
136154
return UIBezierPath(cgPath: self.stripesCGPath(in: rect))
137155
}
156+
157+
func shouldUpdateLayout(_ oldModel: Self) -> Bool {
158+
return self.style != oldModel.style || self.size != oldModel.size
159+
}
160+
}
161+
162+
// MARK: - SwiftUI Helpers
163+
164+
extension ProgressBarVM {
165+
func stripesPath(in rect: CGRect) -> Path {
166+
return Path(self.stripesCGPath(in: rect))
167+
}
138168
}
139169

140170
// MARK: - Validation

Sources/ComponentsKit/ProgressBar/SUProgressBar.swift renamed to Sources/ComponentsKit/Components/ProgressBar/SUProgressBar.swift

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,7 @@ public struct SUProgressBar: View {
1010
@Binding public var currentValue: CGFloat
1111

1212
private var progress: CGFloat {
13-
let range = self.model.maxValue - self.model.minValue
14-
15-
guard range > 0 else {
16-
return 0
17-
}
18-
19-
let progress = (self.currentValue - self.model.minValue) / range
20-
return max(0, min(1, progress))
13+
self.model.progress(for: self.currentValue)
2114
}
2215

2316
// MARK: - Initializer
@@ -40,49 +33,52 @@ public struct SUProgressBar: View {
4033
GeometryReader { geometry in
4134
switch self.model.style {
4235
case .light:
43-
HStack(spacing: 4) {
44-
RoundedRectangle(cornerRadius: self.model.computedCornerRadius)
36+
HStack(spacing: self.model.lightBarSpacing) {
37+
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.progressHeight))
4538
.foregroundStyle(self.model.barColor.color)
4639
.frame(width: geometry.size.width * self.progress)
47-
RoundedRectangle(cornerRadius: self.model.computedCornerRadius)
40+
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.backgroundHeight))
4841
.foregroundStyle(self.model.backgroundColor.color)
4942
.frame(width: geometry.size.width * (1 - self.progress))
5043
}
5144

5245
case .filled:
5346
ZStack(alignment: .leading) {
54-
RoundedRectangle(cornerRadius: self.model.computedCornerRadius)
47+
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.backgroundHeight))
5548
.foregroundStyle(self.model.color.main.color)
5649
.frame(width: geometry.size.width)
5750

58-
RoundedRectangle(cornerRadius: self.model.innerCornerRadius)
51+
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.progressHeight))
5952
.foregroundStyle(self.model.color.contrast.color)
60-
.frame(width: (geometry.size.width - self.model.innerBarPadding * 2) * self.progress)
61-
.padding(.vertical, self.model.innerBarPadding)
62-
.padding(.horizontal, self.model.innerBarPadding)
53+
.frame(width: (geometry.size.width - self.model.progressPadding * 2) * self.progress)
54+
.padding(.vertical, self.model.progressPadding)
55+
.padding(.horizontal, self.model.progressPadding)
6356
}
6457

6558
case .striped:
6659
ZStack(alignment: .leading) {
67-
RoundedRectangle(cornerRadius: self.model.computedCornerRadius)
60+
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.backgroundHeight))
6861
.foregroundStyle(self.model.color.main.color)
6962
.frame(width: geometry.size.width)
7063

71-
RoundedRectangle(cornerRadius: self.model.innerCornerRadius)
64+
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.progressHeight))
7265
.foregroundStyle(self.model.color.contrast.color)
73-
.frame(width: (geometry.size.width - self.model.innerBarPadding * 2) * self.progress)
74-
.padding(.vertical, self.model.innerBarPadding)
75-
.padding(.horizontal, self.model.innerBarPadding)
66+
.frame(width: (geometry.size.width - self.model.progressPadding * 2) * self.progress)
67+
.padding(.vertical, self.model.progressPadding)
68+
.padding(.horizontal, self.model.progressPadding)
7669

7770
StripesShape(model: self.model)
7871
.foregroundStyle(self.model.color.main.color)
79-
.cornerRadius(self.model.computedCornerRadius)
72+
.cornerRadius(self.model.cornerRadius(for: self.model.progressHeight))
8073
.clipped()
8174
}
8275
}
8376
}
84-
.animation(.spring, value: self.progress)
85-
.frame(height: self.model.barHeight)
77+
.animation(
78+
Animation.linear(duration: self.model.animationDuration),
79+
value: self.progress
80+
)
81+
.frame(height: self.model.backgroundHeight)
8682
.onAppear {
8783
self.model.validateMinMaxValues()
8884
}

0 commit comments

Comments
 (0)