-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCustomSpinnerView.swift
More file actions
110 lines (88 loc) · 3.17 KB
/
CustomSpinnerView.swift
File metadata and controls
110 lines (88 loc) · 3.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//
// CustomSpinnerView.swift
// CoreUIKit
//
// Created by Edwin Wilson on 3/28/19.
// Copyright © 2019 Altayer. All rights reserved.
//
// Save to the extent permitted by law, you may not use, copy, modify,
// distribute or create derivative works of this material or any part
// of it without the prior written consent of Altayer.
// Any reproduction of this material must contain this notice.
//
import UIKit
public class CustomSpinnerView: UIView {
private enum Constants {
static let animationDuration = 5.0 // Total animation duration
static let spinnerPerimeterPercentage = 0.85
static let keyAnimationPhase = 10.0 // Total animations in given duartion
}
private let spinnerLayer = CAShapeLayer()
private var spinnerFrameSize: CGSize?
private var keyTime: [Double] = [0.0]
private var rotationValues: [Double] = [0.0]
override public func layoutSubviews() {
super.layoutSubviews()
spinnerLayer.frame = bounds
spinnerLayer.fillColor = nil
spinnerLayer.strokeColor = UIColor.black.cgColor
spinnerLayer.lineWidth = 3
setPath()
layer.addSublayer(spinnerLayer)
}
override public func didMoveToWindow() {
super.didMoveToWindow()
animateSpinner()
}
private func setPath() {
spinnerLayer.path = UIBezierPath(
ovalIn: bounds.insetBy(
dx: spinnerLayer.lineWidth / 2,
dy: spinnerLayer.lineWidth / 2
)
).cgPath
}
private func animateSpinner() {
generateKeyTimesAndRotationValues()
animateStroke()
animateRotation()
}
private func animateStroke() {
let animation = CAKeyframeAnimation(keyPath: "strokeEnd")
var stokeLengthValues = keyTime
stokeLengthValues.append(0.0)
animation.calculationMode = .paced
animateKeypath(fameAnimation: animation, values: stokeLengthValues)
}
private func animateRotation() {
let animation = CAKeyframeAnimation(keyPath: "transform.rotation")
animation.calculationMode = .linear
animateKeypath(fameAnimation: animation, values: rotationValues)
}
private func animateKeypath(
fameAnimation: CAKeyframeAnimation,
values: [Double]
) {
fameAnimation.keyTimes = keyTime as [NSNumber]?
fameAnimation.values = values as [NSNumber]?
fameAnimation.calculationMode = .linear
fameAnimation.duration = Constants.animationDuration
fameAnimation.repeatCount = Float.infinity
spinnerLayer.add(fameAnimation, forKey: fameAnimation.keyPath)
}
private func generateKeyTimesAndRotationValues() {
for i in 1...10 {
let baseTime = Double(i)/10.0
keyTime.append(baseTime)
let nextRotationValue = (rotationValues.last ?? 0.0) + .pi
rotationValues.append(nextRotationValue)
}
//animation to orginal state
if let lastKeyValue = keyTime.last {
keyTime.append(lastKeyValue)
}
if let firstRotaionValue = rotationValues.first {
rotationValues.append(firstRotaionValue)
}
}
}