Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
e506856
UKProgressBar
VislovIvan Dec 30, 2024
484b972
Documentation
VislovIvan Dec 30, 2024
d9b9b81
swiftlint fix
VislovIvan Dec 30, 2024
5bafbbc
renamed layoutNeedsUpdate
VislovIvan Dec 30, 2024
34c6d87
some fix
VislovIvan Dec 30, 2024
99c9756
fix shouldUpdateLayout
VislovIvan Jan 2, 2025
ab22fe9
Preview fix
VislovIvan Jan 2, 2025
dc192aa
progress extracted
VislovIvan Jan 2, 2025
2779d70
updateBarWidth fix
VislovIvan Jan 2, 2025
3c963fa
intrinsicContentSize fix
VislovIvan Jan 2, 2025
ace276a
currentValue renamed to initialValue
VislovIvan Jan 2, 2025
9fcef79
horizontalPadding
VislovIvan Jan 2, 2025
49b8e91
corner radius fix
VislovIvan Jan 2, 2025
76516e9
renaming
VislovIvan Jan 2, 2025
faf4e54
style extension for UKProgressBar
VislovIvan Jan 2, 2025
50bdc32
swiftlint fix
VislovIvan Jan 2, 2025
78c07c7
fix layout metod
VislovIvan Jan 5, 2025
d921d2a
CAShapeLayer for stripedLayer instead of UIView
VislovIvan Jan 5, 2025
84abff2
fix animation
VislovIvan Jan 5, 2025
5f90099
code style fix
VislovIvan Jan 5, 2025
9a2c02e
some fix
VislovIvan Jan 7, 2025
1a7b716
Merge branch 'SUProgressBar' into UKProgressBar
VislovIvan Jan 7, 2025
058e95a
swiftlint fix
VislovIvan Jan 7, 2025
9d2ba44
Merge branch 'UKProgressBar' of https://github.com/componentskit/Comp…
VislovIvan Jan 7, 2025
cb9babd
fix merge conflicts
VislovIvan Jan 7, 2025
0d1b482
move ProgressBar folder into Components
mikhailChelbaev Jan 7, 2025
263a302
improve layout in UKProgressBar
mikhailChelbaev Jan 7, 2025
4e22f5b
fix progress bar preview
mikhailChelbaev Jan 7, 2025
c10bc32
improve helpers to calculate corner radius in progress bar
mikhailChelbaev Jan 7, 2025
fcc588d
simplify code in progress bar
mikhailChelbaev Jan 7, 2025
f8fab4d
improve method's name
mikhailChelbaev Jan 7, 2025
7ae6885
validate min max values in UKProgressBar
mikhailChelbaev Jan 8, 2025
06e613b
change animation style in progress bar to `linear`
mikhailChelbaev Jan 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,28 @@ import SwiftUI
import UIKit

struct ProgressBarPreview: View {
@State private var model = ProgressBarVM()
@State private var currentValue: CGFloat = 0
@State private var model = Self.initialModel
@State private var currentValue: CGFloat = Self.initialValue

private let progressBar = UKProgressBar(initialValue: Self.initialValue, model: Self.initialModel)

private let timer = Timer
.publish(every: 0.1, on: .main, in: .common)
.autoconnect()

var body: some View {
VStack {
PreviewWrapper(title: "UIKit") {
self.progressBar
.preview
.onAppear {
self.progressBar.currentValue = self.currentValue
self.progressBar.model = Self.initialModel
}
.onChange(of: self.model) { newValue in
self.progressBar.model = newValue
}
}
PreviewWrapper(title: "SwiftUI") {
SUProgressBar(currentValue: self.$currentValue, model: self.model)
}
Expand All @@ -33,8 +47,19 @@ struct ProgressBarPreview: View {
} else {
self.currentValue = self.model.minValue
}

self.progressBar.currentValue = self.currentValue
}
}

// MARK: - Helpers

private static var initialValue: Double {
return 0.0
}
private static var initialModel: ProgressBarVM {
return .init()
}
}

#Preview {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@ public struct ProgressBarVM: ComponentVM {
// MARK: - Shared Helpers

extension ProgressBarVM {
var innerBarPadding: CGFloat {
return 3
}

var barHeight: CGFloat {
var backgroundHeight: CGFloat {
switch self.style {
case .light:
switch size {
Expand All @@ -62,25 +58,42 @@ extension ProgressBarVM {
}
}

var computedCornerRadius: CGFloat {
var progressHeight: CGFloat {
return self.backgroundHeight - self.progressPadding
}

func cornerRadius(for height: CGFloat) -> CGFloat {
switch self.cornerRadius {
case .none:
return 0.0
return 0
case .small:
return self.barHeight / 3.5
return height / 3.5
case .medium:
return self.barHeight / 3.0
return height / 3.0
case .large:
return self.barHeight / 2.5
return height / 2.5
case .full:
return self.barHeight / 2.0
return height / 2.0
case .custom(let value):
return min(value, self.barHeight / 2)
return min(value, height / 2)
}
}

var innerCornerRadius: CGFloat {
return max(0, self.computedCornerRadius - self.innerBarPadding)
var animationDuration: TimeInterval {
return 0.2
}

var progressPadding: CGFloat {
switch self.style {
case .light:
return 0
case .filled, .striped:
return 3
}
}

var lightBarSpacing: CGFloat {
return 4
}

var backgroundColor: UniversalColor {
Expand All @@ -101,10 +114,6 @@ extension ProgressBarVM {
}
}

func shouldUpdateLayout(_ oldModel: Self) -> Bool {
return self.size != oldModel.size
}

private func stripesCGPath(in rect: CGRect) -> CGMutablePath {
let stripeWidth: CGFloat = 2
let stripeSpacing: CGFloat = 4
Expand All @@ -127,14 +136,35 @@ extension ProgressBarVM {
}
return path
}
}

public func stripesPath(in rect: CGRect) -> Path {
return Path(self.stripesCGPath(in: rect))
extension ProgressBarVM {
func progress(for currentValue: CGFloat) -> CGFloat {
let range = self.maxValue - self.minValue
guard range > 0 else { return 0 }
let normalized = (currentValue - self.minValue) / range
return max(0, min(1, normalized))
}
}

public func stripesBezierPath(in rect: CGRect) -> UIBezierPath {
// MARK: - UIKit Helpers

extension ProgressBarVM {
func stripesBezierPath(in rect: CGRect) -> UIBezierPath {
return UIBezierPath(cgPath: self.stripesCGPath(in: rect))
}

func shouldUpdateLayout(_ oldModel: Self) -> Bool {
return self.style != oldModel.style || self.size != oldModel.size
}
}

// MARK: - SwiftUI Helpers

extension ProgressBarVM {
func stripesPath(in rect: CGRect) -> Path {
return Path(self.stripesCGPath(in: rect))
}
}

// MARK: - Validation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,7 @@ public struct SUProgressBar: View {
@Binding public var currentValue: CGFloat

private var progress: CGFloat {
let range = self.model.maxValue - self.model.minValue

guard range > 0 else {
return 0
}

let progress = (self.currentValue - self.model.minValue) / range
return max(0, min(1, progress))
self.model.progress(for: self.currentValue)
}

// MARK: - Initializer
Expand All @@ -40,49 +33,52 @@ public struct SUProgressBar: View {
GeometryReader { geometry in
switch self.model.style {
case .light:
HStack(spacing: 4) {
RoundedRectangle(cornerRadius: self.model.computedCornerRadius)
HStack(spacing: self.model.lightBarSpacing) {
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.progressHeight))
.foregroundStyle(self.model.barColor.color)
.frame(width: geometry.size.width * self.progress)
RoundedRectangle(cornerRadius: self.model.computedCornerRadius)
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.backgroundHeight))
.foregroundStyle(self.model.backgroundColor.color)
.frame(width: geometry.size.width * (1 - self.progress))
}

case .filled:
ZStack(alignment: .leading) {
RoundedRectangle(cornerRadius: self.model.computedCornerRadius)
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.backgroundHeight))
.foregroundStyle(self.model.color.main.color)
.frame(width: geometry.size.width)

RoundedRectangle(cornerRadius: self.model.innerCornerRadius)
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.progressHeight))
.foregroundStyle(self.model.color.contrast.color)
.frame(width: (geometry.size.width - self.model.innerBarPadding * 2) * self.progress)
.padding(.vertical, self.model.innerBarPadding)
.padding(.horizontal, self.model.innerBarPadding)
.frame(width: (geometry.size.width - self.model.progressPadding * 2) * self.progress)
.padding(.vertical, self.model.progressPadding)
.padding(.horizontal, self.model.progressPadding)
}

case .striped:
ZStack(alignment: .leading) {
RoundedRectangle(cornerRadius: self.model.computedCornerRadius)
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.backgroundHeight))
.foregroundStyle(self.model.color.main.color)
.frame(width: geometry.size.width)

RoundedRectangle(cornerRadius: self.model.innerCornerRadius)
RoundedRectangle(cornerRadius: self.model.cornerRadius(for: self.model.progressHeight))
.foregroundStyle(self.model.color.contrast.color)
.frame(width: (geometry.size.width - self.model.innerBarPadding * 2) * self.progress)
.padding(.vertical, self.model.innerBarPadding)
.padding(.horizontal, self.model.innerBarPadding)
.frame(width: (geometry.size.width - self.model.progressPadding * 2) * self.progress)
.padding(.vertical, self.model.progressPadding)
.padding(.horizontal, self.model.progressPadding)

StripesShape(model: self.model)
.foregroundStyle(self.model.color.main.color)
.cornerRadius(self.model.computedCornerRadius)
.cornerRadius(self.model.cornerRadius(for: self.model.progressHeight))
.clipped()
}
}
}
.animation(.spring, value: self.progress)
.frame(height: self.model.barHeight)
.animation(
Animation.linear(duration: self.model.animationDuration),
value: self.progress
)
.frame(height: self.model.backgroundHeight)
.onAppear {
self.model.validateMinMaxValues()
}
Expand Down
Loading
Loading