Skip to content

Commit f98f23d

Browse files
UNT-T11872 progress view animation
1 parent 3c9f828 commit f98f23d

File tree

8 files changed

+721
-0
lines changed

8 files changed

+721
-0
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// ArrowView.swift
3+
// SSSwiftUIAnimations
4+
//
5+
// Created by Mansi Prajapati on 15/05/24.
6+
//
7+
8+
import SwiftUI
9+
10+
struct Arrow: View {
11+
12+
// MARK: - Variables
13+
var arrowStyle: ArrowViewParams
14+
var style: SSProgressViewStyle
15+
@Binding var progress: Float
16+
17+
var body: some View {
18+
VStack(spacing: 0) {
19+
20+
// Vertical line of arrow
21+
RoundedRectangle(cornerRadius: style.circleStrokeWidth/2)
22+
.frame(
23+
width: arrowStyle.isAnimating ? style.circleStrokeWidth : style.arrowStrokeWidth + 1,
24+
height: arrowStyle.isAnimating ? (!arrowStyle.animationStarted ? style.circleStrokeWidth : style.circleStrokeWidth ) : style.circleSize * 0.40
25+
)
26+
.opacity(arrowStyle.showVerticalLine ? 1 : 0)
27+
.foregroundColor(style.arrowColor)
28+
.offset(y: arrowStyle.isAnimating ? (!arrowStyle.animationStarted ? -5 : -(style.circleSize/2)) : 0)
29+
.animation(Animation.linear.speed(!arrowStyle.animationStarted ? 100 : .zero), value: 0)
30+
31+
if progress < 0.1 {
32+
33+
// if progress is less than 0.1 then showing down arrow animation
34+
DownArrow(
35+
initialAnim: arrowStyle.initialAnim,
36+
isDownward: arrowStyle.isDownward,
37+
progress: Float(progress),
38+
circleSize: style.circleSize,
39+
isAnimating: arrowStyle.isAnimating
40+
)
41+
.stroke(style: StrokeStyle(lineWidth: style.arrowStrokeWidth, lineCap: .round))
42+
.frame(width: 10, height: 50)
43+
.padding(.top, -50)
44+
.offset(y: arrowStyle.isAnimating ? 22 : .zero)
45+
.foregroundColor(style.arrowColor)
46+
.scaleEffect(CGSize(width: 1, height: arrowStyle.isAnimating ? 1 : style.circleSize * 0.005))
47+
} else if progress >= 0.1 && progress < 1.0 {
48+
49+
// if progress is between >= 0.1 and < 1.0 starting wave animation
50+
WaveView(
51+
progress: $progress,
52+
arrowStrokeWidth: style.arrowStrokeWidth,
53+
width: style.circleSize
54+
)
55+
.frame(width: 10, height: 30)
56+
.foregroundColor(style.arrowColor)
57+
} else if progress >= 1.0 {
58+
59+
// if progress completed showing check mark
60+
CheckView(
61+
width: style.circleSize,
62+
arrowStrokeWidth: style.arrowStrokeWidth
63+
)
64+
.padding(.top, -11)
65+
.offset(y: 22)
66+
.foregroundColor(style.arrowColor)
67+
}
68+
}
69+
}
70+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//
2+
// CheckView.swift
3+
// SSSwiftUIAnimations
4+
//
5+
// Created by Mansi Prajapati on 16/04/24.
6+
//
7+
8+
import SwiftUI
9+
10+
struct CheckView: View {
11+
12+
// MARK: - Variables
13+
14+
// For drawing straight line before check mark
15+
@State private var x = 0.92
16+
@State private var y = 2.5
17+
18+
// Width of line and check mark
19+
@State var width: CGFloat = 0
20+
21+
// For scale and offset of check mark
22+
@State private var size: CGFloat = 0
23+
@State private var isAnimating = false
24+
@State private var straight: CGFloat = 0
25+
@State private var offset = 0
26+
27+
var arrowStrokeWidth: CGFloat
28+
29+
var body: some View {
30+
ZStack(alignment:.center) {
31+
VStack {
32+
Check(endPoint: CGPoint(
33+
x: isAnimating ? 0.3 : x,
34+
y: isAnimating ? 4.5 : y),
35+
height: size,
36+
stright: straight)
37+
.stroke(style: StrokeStyle(lineWidth: isAnimating ? arrowStrokeWidth / (size * 0.01) : arrowStrokeWidth, lineCap: .round))
38+
.frame(width: width * 0.57, height: 105)
39+
.offset(CGSize(width: 0, height: isAnimating ? offset : 0))
40+
}.scaleEffect(
41+
CGSize(
42+
width: isAnimating ? size * 0.01 : 1,
43+
height: isAnimating ? size * 0.01 : 1
44+
), anchor: .center
45+
)
46+
}.onAppear() {
47+
size = width
48+
offset = size <= 150 ? -10 : -1
49+
50+
// For changing width once it turns from line to check mark
51+
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
52+
withAnimation(.linear(duration: 0.5)) {
53+
isAnimating = true
54+
width = 65
55+
}
56+
}
57+
58+
// For increasing the Y of check mark
59+
DispatchQueue.main.asyncAfter(deadline: .now() + 1.1) {
60+
straight = 18.0
61+
}
62+
}
63+
}
64+
}
65+
66+
struct Check: Shape {
67+
68+
// MARK: - Variables
69+
var endPoint: CGPoint
70+
var height: CGFloat
71+
var stright: CGFloat
72+
73+
// MARK: - Animatable data
74+
var animatableData: AnimatablePair<CGFloat, CGFloat> {
75+
get { AnimatablePair(endPoint.x, endPoint.y) }
76+
set {
77+
endPoint.x = newValue.first
78+
endPoint.y = newValue.second
79+
}
80+
}
81+
82+
func path(in rect: CGRect) -> Path {
83+
let rightLine = height < 150 ? 1.07 : 1.10
84+
let start = CGPoint(
85+
x: rect.midX * 0.23,
86+
y: rect.midY - 21 * endPoint.x - 0.2
87+
)
88+
let end = CGPoint(
89+
x: rect.maxX/2,
90+
y: rect.maxY * endPoint.y / 8
91+
)
92+
let start2 = CGPoint(
93+
x: rect.maxX/rightLine,
94+
y: rect.midY - 21 * endPoint.x - stright
95+
)
96+
var path = Path()
97+
path.move(to: start)
98+
path.addLine(to: end)
99+
path.move(to: start2)
100+
path.addLine(to: end)
101+
return path
102+
}
103+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
//
2+
// DownArrow.swift
3+
// SSSwiftUIAnimations
4+
//
5+
// Created by Mansi Prajapati on 06/05/24.
6+
//
7+
8+
import SwiftUI
9+
10+
struct DownArrow: Shape {
11+
12+
// MARK: - Variables
13+
var initialAnim: CGFloat
14+
var isDownward: Bool
15+
var progress: Float
16+
var circleSize: CGFloat
17+
var isAnimating: Bool
18+
19+
// MARK: - Animatable Data
20+
var animatableData: CGFloat {
21+
get { initialAnim }
22+
set { initialAnim = newValue }
23+
}
24+
25+
func path(in rect: CGRect) -> Path {
26+
var path = Path()
27+
let startX = rect.midX - circleSize * (isAnimating ? 0.12 : 0.13)
28+
let endX = rect.midX + circleSize * (isAnimating ? 0.12 : 0.12)
29+
let centerY = rect.midY
30+
var controlY: CGFloat
31+
32+
if isDownward {
33+
controlY = centerY + (initialAnim * (rect.height / 2))
34+
} else {
35+
let bounceProgress = bounce(initialAnim)
36+
controlY = centerY - (bounceProgress * (rect.height / 4))
37+
}
38+
39+
let controlX = rect.midX
40+
path.move(to: CGPoint(x: startX, y: centerY))
41+
42+
if !isDownward {
43+
// Curve for bounce effect
44+
path.move(to: CGPoint(x: startX - circleSize * 0.1, y: centerY))
45+
path.addQuadCurve(
46+
to: CGPoint(x: rect.maxX + circleSize * 0.21, y: centerY),
47+
control: CGPoint(x: controlX, y: controlY)
48+
)
49+
} else if initialAnim <= 1.0 {
50+
// Down arrow
51+
path.addLine(to: CGPoint(x: rect.midX, y: controlY))
52+
path.addLine(to: CGPoint(x: endX, y: centerY))
53+
} else if progress < 0.1 {
54+
// Straight line
55+
path.move(to: CGPoint(x: startX - circleSize * 0.1, y: centerY))
56+
path.addLine(to: CGPoint(x: rect.maxX + circleSize * 0.21, y: centerY))
57+
}
58+
return path
59+
}
60+
61+
// MARK: - Private functions
62+
63+
// Function to apply bounce effect
64+
private func bounce(_ progress: CGFloat) -> CGFloat {
65+
let numberOfBounces = 2
66+
let bounces = CGFloat(numberOfBounces)
67+
return abs((circleSize * 0.01 - progress) * sin(progress * .pi * bounces))
68+
}
69+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//
2+
// ExampleProgressView.swift
3+
// SSSwiftUIAnimations
4+
//
5+
// Created by Mansi Prajapati on 17/05/24.
6+
//
7+
8+
import SwiftUI
9+
10+
struct ExampleProgressView: View {
11+
12+
// MARK: - Variables
13+
@State private var progress: Float = 0.0
14+
@State var timer: Timer?
15+
16+
var body: some View {
17+
VStack {
18+
SSProgressView(progress: $progress, style: SSProgressViewStyle(circleSize: 200, circleStrokeWidth: 5, arrowStrokeWidth: 5, progressTextColor: .pink, fillStrokeColor: .blue, arrowColor: .blue, allowCancelProgress: true), onProgressViewClick: {
19+
simulateDownloadProgress()
20+
}, onProgressCompletion: {
21+
timer?.invalidate()
22+
}, onCancelProgress: {
23+
progress = 0.0
24+
timer?.invalidate()
25+
})
26+
}.navigationBarTitle("ProgressView Example", displayMode: .inline)
27+
}
28+
29+
// MARK: - Private functions
30+
private func simulateDownloadProgress() {
31+
timer = Timer.scheduledTimer(withTimeInterval: 0.2, repeats: true) { timer in
32+
if progress >= 1.0 {
33+
timer.invalidate()
34+
} else {
35+
progress += 0.01
36+
}
37+
}
38+
}
39+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//
2+
// ModelClass.swift
3+
// SSSwiftUIAnimations
4+
//
5+
// Created by Mansi Prajapati on 14/05/24.
6+
//
7+
8+
import SwiftUI
9+
10+
struct SSProgressViewStyle {
11+
var circleSize: CGFloat
12+
var circleStrokeWidth: CGFloat
13+
var arrowStrokeWidth: CGFloat
14+
var fontSize: CGFloat
15+
var fontWeight: Font.Weight
16+
var progressTextColor: Color
17+
var emptyStrokeColor: Color
18+
var fillStrokeColor: Color
19+
var arrowColor: Color
20+
var allowCancelProgress: Bool
21+
22+
init(
23+
circleSize: CGFloat = 200.0,
24+
circleStrokeWidth: CGFloat = 5.0,
25+
arrowStrokeWidth: CGFloat = 5.0,
26+
fontSize: CGFloat = 15.0,
27+
fontWeight: Font.Weight = .regular,
28+
progressTextColor: Color = Color.blue,
29+
emptyStrokeColor: Color = Color.blue.opacity(0.2),
30+
fillStrokeColor: Color = Color.blue,
31+
arrowColor: Color = Color.blue,
32+
allowCancelProgress: Bool = false
33+
) {
34+
35+
// Circle size
36+
self.circleSize = max(circleSize, 100)
37+
38+
// Circle stroke width
39+
self.circleStrokeWidth = circleStrokeWidth
40+
41+
// Arrow stroke width
42+
self.arrowStrokeWidth = arrowStrokeWidth
43+
44+
// Progress text font size
45+
self.fontSize = fontSize
46+
47+
// Progress text fontWeight
48+
self.fontWeight = fontWeight
49+
50+
// Color of progress text percentage
51+
self.progressTextColor = progressTextColor
52+
53+
// Circle's empty stroke's color
54+
self.emptyStrokeColor = emptyStrokeColor
55+
56+
// Circle's fill stroke's color
57+
self.fillStrokeColor = fillStrokeColor
58+
59+
// Arrow color
60+
self.arrowColor = arrowColor
61+
62+
// want to allow cancellation of progress or not
63+
self.allowCancelProgress = allowCancelProgress
64+
}
65+
}
66+
67+
struct ArrowViewParams {
68+
var isAnimating: Bool
69+
var initialAnim: CGFloat
70+
var isDownward: Bool
71+
var animationStarted: Bool
72+
var showVerticalLine: Bool
73+
var showPercent: Bool
74+
75+
init(
76+
isAnimating: Bool = false,
77+
progress: Double = 0.0,
78+
doneAnimating: Bool = false,
79+
initialAnim: CGFloat = 1.0,
80+
isDownward: Bool = true,
81+
animationStarted: Bool = true,
82+
showVerticalLine: Bool = true,
83+
showPercent: Bool = false
84+
) {
85+
self.isAnimating = isAnimating
86+
self.initialAnim = initialAnim
87+
self.isDownward = isDownward
88+
self.animationStarted = animationStarted
89+
self.showVerticalLine = showVerticalLine
90+
self.showPercent = showPercent
91+
}
92+
}
93+

0 commit comments

Comments
 (0)