Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -6,36 +6,24 @@ struct CircularProgressPreview: View {
@State private var model = Self.initialModel
@State private var currentValue: CGFloat = Self.initialValue

private let circularProgress = UKCircularProgress(
model: Self.initialModel
)

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

var body: some View {
VStack {
PreviewWrapper(title: "UIKit") {
self.circularProgress
.preview
.onAppear {
self.circularProgress.currentValue = Self.initialValue
self.circularProgress.model = Self.initialModel
}
.onChange(of: model) { newModel in
self.circularProgress.model = newModel
}
.onChange(of: self.currentValue) { newValue in
self.circularProgress.currentValue = newValue
}
}
PreviewWrapper(title: "SwiftUI") {
SUCircularProgress(currentValue: self.currentValue, model: self.model)
}
Form {
ComponentColorPicker(selection: self.$model.color)
CaptionFontPicker(selection: self.$model.font)
Picker("Font", selection: self.$model.font) {
Text("Default").tag(Optional<UniversalFont>.none)
Text("Small").tag(UniversalFont.smButton)
Text("Medium").tag(UniversalFont.mdButton)
Text("Large").tag(UniversalFont.lgButton)
Text("Custom: system bold of size 16").tag(UniversalFont.system(size: 16, weight: .bold))
}
Picker("Line Width", selection: self.$model.lineWidth) {
Text("Default").tag(Optional<CGFloat>.none)
Text("2").tag(Optional<CGFloat>.some(2))
Expand Down Expand Up @@ -68,9 +56,8 @@ struct CircularProgressPreview: View {
private static var initialValue: Double {
return 0.0
}

private static var initialModel = CircularProgressVM {
$0.label = "0%"
$0.label = "0"
$0.style = .light
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,24 +81,24 @@ extension CircularProgressVM {
return .lgCaption
}
}
var stripeWidth: CGFloat {
return 0.5
}
private func stripesCGPath(in rect: CGRect) -> CGMutablePath {
let stripeWidth: CGFloat = 0.5
let stripeSpacing: CGFloat = 3
let stripeAngle: Angle = .degrees(135)

let path = CGMutablePath()
let step = stripeWidth + stripeSpacing
let radians = stripeAngle.radians

let dx: CGFloat = rect.height * tan(radians)
for x in stride(from: 0, through: rect.width + rect.height, by: step) {
let dx = rect.height * tan(radians)
for x in stride(from: dx, through: rect.width + rect.height, by: step) {
let topLeft = CGPoint(x: x, y: 0)
let bottomRight = CGPoint(x: x + dx, y: rect.height)

let topRight = CGPoint(x: x + stripeWidth, y: 0)
let bottomLeft = CGPoint(x: x + dx, y: rect.height)
let bottomRight = CGPoint(x: x + stripeWidth + dx, y: rect.height)
path.move(to: topLeft)
path.addLine(to: topRight)
path.addLine(to: bottomRight)
path.addLine(to: bottomLeft)
path.closeSubpath()
}
return path
Expand All @@ -107,71 +107,37 @@ extension CircularProgressVM {

extension CircularProgressVM {
func gap(for normalized: CGFloat) -> CGFloat {
return normalized > 0 ? 0.05 : 0
normalized > 0 ? 0.05 : 0
}

func stripedArcStart(for normalized: CGFloat) -> CGFloat {
func progressArcStart(for normalized: CGFloat) -> CGFloat {
return 0
}

func progressArcEnd(for normalized: CGFloat) -> CGFloat {
return max(0, min(1, normalized))
}

func backgroundArcStart(for normalized: CGFloat) -> CGFloat {
let gapValue = self.gap(for: normalized)
return max(0, min(1, normalized + gapValue))
}

func stripedArcEnd(for normalized: CGFloat) -> CGFloat {
func backgroundArcEnd(for normalized: CGFloat) -> CGFloat {
let gapValue = self.gap(for: normalized)
return 1 - gapValue
}
}

extension CircularProgressVM {
func progress(for currentValue: CGFloat) -> CGFloat {
public 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))
}
}

// MARK: - UIKit Helpers

extension CircularProgressVM {
var isStripesLayerHidden: Bool {
switch self.style {
case .light:
return true
case .striped:
return false
}
}
var isBackgroundLayerHidden: Bool {
switch self.style {
case .light:
return false
case .striped:
return true
}
}
func stripesBezierPath(in rect: CGRect) -> UIBezierPath {
let center = CGPoint(x: rect.midX, y: rect.midY)
let path = UIBezierPath(cgPath: self.stripesCGPath(in: rect))
var transform = CGAffineTransform.identity
transform = transform
.translatedBy(x: center.x, y: center.y)
.rotated(by: -CGFloat.pi / 2)
.translatedBy(x: -center.x, y: -center.y)
path.apply(transform)
return path
}
func shouldInvalidateIntrinsicContentSize(_ oldModel: Self) -> Bool {
return self.preferredSize != oldModel.preferredSize
}
func shouldUpdateText(_ oldModel: Self) -> Bool {
return self.label != oldModel.label
}
func shouldRecalculateProgress(_ oldModel: Self) -> Bool {
return self.minValue != oldModel.minValue
|| self.maxValue != oldModel.maxValue
}
}

// MARK: - SwiftUI Helpers

extension CircularProgressVM {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,33 +102,52 @@ public struct SUCircularProgress: View {
}

var stripedBackground: some View {
StripesShapeCircularProgress(model: self.model)
.stroke(
self.model.color.main.color,
style: StrokeStyle(lineWidth: self.model.stripeWidth)
Path { path in
path.addArc(
center: self.model.center,
radius: self.model.radius,
startAngle: .radians(0),
endAngle: .radians(2 * .pi),
clockwise: false
)
.mask {
Path { maskPath in
maskPath.addArc(
center: self.model.center,
radius: self.model.radius,
startAngle: .radians(0),
endAngle: .radians(2 * .pi),
clockwise: false
}
.trim(
from: self.model.backgroundArcStart(for: self.progress),
to: self.model.backgroundArcEnd(for: self.progress)
)
.stroke(
.clear,
style: StrokeStyle(
lineWidth: self.model.circularLineWidth,
lineCap: .round
)
)
.overlay {
StripesShapeCircularProgress(model: self.model)
.foregroundColor(self.model.color.main.color)
.mask {
Path { maskPath in
maskPath.addArc(
center: self.model.center,
radius: self.model.radius,
startAngle: .radians(0),
endAngle: .radians(2 * .pi),
clockwise: false
)
}
.trim(
from: self.model.backgroundArcStart(for: self.progress),
to: self.model.backgroundArcEnd(for: self.progress)
)
}
.trim(
from: self.model.stripedArcStart(for: self.progress),
to: self.model.stripedArcEnd(for: self.progress)
)
.stroke(
style: StrokeStyle(
lineWidth: self.model.circularLineWidth,
lineCap: .round
.stroke(
style: StrokeStyle(
lineWidth: self.model.circularLineWidth,
lineCap: .round
)
)
)
}
.rotationEffect(.degrees(-90))
}
}
.rotationEffect(.degrees(-90))
}
}

Expand Down
Loading
Loading