Skip to content

Commit

Permalink
Circular Progress bar
Browse files Browse the repository at this point in the history
  • Loading branch information
brijeshbarasiya2022 committed Apr 19, 2024
1 parent 1ce5968 commit 54585e5
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
22AA9A5D2B67A065002FC677 /* Stick.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AA9A5C2B67A065002FC677 /* Stick.swift */; };
22AA9A5F2B67A0DA002FC677 /* CircularProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AA9A5E2B67A0DA002FC677 /* CircularProgressBar.swift */; };
22AA9A612B67A0EF002FC677 /* LinearProgressBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22AA9A602B67A0EF002FC677 /* LinearProgressBar.swift */; };
22C2680E2B8F284100ECC448 /* SwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22C2680D2B8F284100ECC448 /* SwiftUIView.swift */; };
2BC2D8F328CF3A6F00CAB302 /* SSSwiftUIAnimationsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BC2D8F228CF3A6F00CAB302 /* SSSwiftUIAnimationsApp.swift */; };
2BC2D8F528CF3A6F00CAB302 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2BC2D8F428CF3A6F00CAB302 /* ContentView.swift */; };
2BC2D8F728CF3A7000CAB302 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2BC2D8F628CF3A7000CAB302 /* Assets.xcassets */; };
Expand All @@ -30,6 +31,7 @@
22AA9A5C2B67A065002FC677 /* Stick.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stick.swift; sourceTree = "<group>"; };
22AA9A5E2B67A0DA002FC677 /* CircularProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularProgressBar.swift; sourceTree = "<group>"; };
22AA9A602B67A0EF002FC677 /* LinearProgressBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinearProgressBar.swift; sourceTree = "<group>"; };
22C2680D2B8F284100ECC448 /* SwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIView.swift; sourceTree = "<group>"; };
2BC2D8EF28CF3A6F00CAB302 /* SSSwiftUIAnimations.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SSSwiftUIAnimations.app; sourceTree = BUILT_PRODUCTS_DIR; };
2BC2D8F228CF3A6F00CAB302 /* SSSwiftUIAnimationsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SSSwiftUIAnimationsApp.swift; sourceTree = "<group>"; };
2BC2D8F428CF3A6F00CAB302 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -75,6 +77,7 @@
children = (
22AA9A602B67A0EF002FC677 /* LinearProgressBar.swift */,
22AA9A5E2B67A0DA002FC677 /* CircularProgressBar.swift */,
22C2680D2B8F284100ECC448 /* SwiftUIView.swift */,
);
path = ProgressBar;
sourceTree = "<group>";
Expand Down Expand Up @@ -187,6 +190,7 @@
files = (
2BC2D8F528CF3A6F00CAB302 /* ContentView.swift in Sources */,
22AA9A612B67A0EF002FC677 /* LinearProgressBar.swift in Sources */,
22C2680E2B8F284100ECC448 /* SwiftUIView.swift in Sources */,
22AA9A572B679FEC002FC677 /* LoadingObservable.swift in Sources */,
22AA9A5F2B67A0DA002FC677 /* CircularProgressBar.swift in Sources */,
22AA9A5B2B67A03B002FC677 /* LinearLoading.swift in Sources */,
Expand Down
7 changes: 5 additions & 2 deletions SSSwiftUIAnimations/SSSwiftUIAnimations/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@ import SwiftUI

struct ContentView: View {
var body: some View {
Text("Hello, world!")
.padding()
VStack {
LoadingView(loaderType: .linearProgressBar(percentange: .constant(45), stickHeight: 100))
.frame(height: 100)
Text("Hello")
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
//
// SwiftUIView.swift
// SSSwiftUIAnimations
//
// Created by Brijesh Barasiya on 28/02/24.
//

import SwiftUI

struct SwiftUIView: View {
@Binding private var percentage: Float
@State private var sticks: [Stick]
@State private var lastStickIndex: Int = 0
private let circleRadius: CGFloat
private let stickWidth: CGFloat
private let filledColor: Color
private let unFilledColor: Color
private let duration: Double
private let progressColor: Color

init(
percentage: Binding<Float>,
size: Float,
stickWidth: Float,
progressColor: Color,
filledColor: Color,
unFilledColor: Color,
duration: Double
) {
let screenBounds = UIScreen.main.bounds
let screenWidth = Float(screenBounds.width)
let screenHeight = Float(screenBounds.height)

// Adjust stick size, ensuring it doesn't exceed the screen size - 50
let adjustedSize: Float = min(max(size, 50), min(screenWidth, screenHeight) - 50)

// Adjust stick width, ensuring a minimum value of 9% of size.
let adjustedStickWidth = min(max((adjustedSize / 100) * 5, stickWidth), (adjustedSize / 100) * 20)

let circumference = 2 * .pi * adjustedSize
let spacing = circumference / adjustedStickWidth
let totalStickCount: Int = Int((spacing * 25) / 100)
self.sticks = Array(repeating: Stick(xAxis: 0, color: unFilledColor), count: totalStickCount)
self.circleRadius = CGFloat(adjustedSize/2)
self.stickWidth = CGFloat(adjustedStickWidth)
self.progressColor = progressColor
self.filledColor = filledColor
self.unFilledColor = unFilledColor
self.duration = duration / Double(totalStickCount)
self._percentage = percentage
}

var body: some View {
Circle()
.frame(width: circleRadius * 2)
.foregroundColor(Color.clear)
.overlay {
ForEach(0..<sticks.count, id: \.self) { index in
Rectangle()
.frame(width: stickWidth, height: (stickWidth * 3))
.foregroundColor(sticks[index].color)
.offset(y: (circleRadius - stickWidth))
.rotationEffect(
.degrees(Double((CGFloat(index) + sticks[index].xAxis) * 360) / Double(sticks.count))
)
}
}
.onAppear {
animateStickView(index: 0, isReversing: false)
}
}

private func animateStickView(index: Int, isReversing: Bool) {
if #available(iOS 17.0, *) {
withAnimation(Animation.linear(duration: duration)) {
updateStickViewProperties(index: index, isReversing: isReversing)
} completion: {
resertStickViewAnimation(index: index, isReversing: isReversing)
}
} else {
withAnimation(Animation.linear(duration: duration)) {
updateStickViewProperties(index: index, isReversing: isReversing)
}
DispatchQueue.main.asyncAfter(deadline: .now() + duration) {
resertStickViewAnimation(index: index, isReversing: isReversing)
}
}
}

private func updateStickViewProperties(index: Int, isReversing: Bool) {
sticks[index].xAxis = isReversing ? -0.6 : 0.6
sticks[index].color = getStickColor(forIndex: index, isReversing: isReversing)
let validatedPercentage = min(max(0, percentage), 100)
let sticksAccordingToPercentage = Double(sticks.count) * Double(validatedPercentage / 100)
let numberOfSticksToChange = max(Int(sticksAccordingToPercentage), 0)
for stickIndex in 0..<min(numberOfSticksToChange, index) {
sticks[stickIndex].color = progressColor
}
let finalIndex = index >= numberOfSticksToChange ? numberOfSticksToChange : lastStickIndex
lastStickIndex = finalIndex
}

private func resertStickViewAnimation(index: Int, isReversing: Bool) {
withAnimation(Animation.linear(duration: duration)) {
sticks[index].xAxis = 0
}
let nextIndex = isReversing ? index - 1 : index + 1
if (index == (lastStickIndex) && isReversing) || (index == sticks.indices.last && !isReversing) {
animateStickView(index: isReversing ? nextIndex + 1 : nextIndex - 1, isReversing: !isReversing)
} else {
animateStickView(index: nextIndex ,isReversing: isReversing)
}
}

private func getStickColor(forIndex index: Int, isReversing: Bool) -> Color {
return if (index == lastStickIndex) {
filledColor
} else if (index == sticks.indices.last) {
unFilledColor
} else {
(isReversing ? unFilledColor : filledColor)
}
}
}

#Preview {
SwiftUIView(
percentage: .constant(76),
size: 100,
stickWidth: 3,
progressColor: .green,
filledColor: .blue,
unFilledColor: .gray,
duration: 5
)
}

0 comments on commit 54585e5

Please sign in to comment.