forked from LoopKit/LoopKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgressIndicatorView.swift
More file actions
152 lines (126 loc) · 4.81 KB
/
ProgressIndicatorView.swift
File metadata and controls
152 lines (126 loc) · 4.81 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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//
// ProgressIndicatorView.swift
// DashKitUI
//
// Created by Pete Schwamb on 3/2/20.
// Copyright © 2020 LoopKit Authors. All rights reserved.
//
import SwiftUI
import Combine
public enum ProgressIndicatorState: Equatable {
case hidden
case indeterminantProgress
case timedProgress(finishTime: CFTimeInterval)
case completed
}
extension ProgressIndicatorState {
var showProgressBar: Bool {
if case .timedProgress = self {
return true
}
return false
}
var showIndeterminantProgress: Bool {
if case .indeterminantProgress = self {
return true
}
return false
}
var showCompletion: Bool {
if case .completed = self {
return true
}
return false
}
}
public struct ProgressIndicatorView: View {
private let state: ProgressIndicatorState
private let fullSize: CGFloat = 35
// timed progress
private let timer: Publishers.Autoconnect<Timer.TimerPublisher>
@State private var progress: Double = 0
private let startTime: CFTimeInterval
private var finishTime: CFTimeInterval
private var duration: TimeInterval {
return max(0, finishTime - startTime)
}
public init(state: ProgressIndicatorState) {
startTime = CACurrentMediaTime()
self.state = state
timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
if case .timedProgress(let finishTime) = state {
self.finishTime = finishTime
} else {
timer.upstream.connect().cancel()
self.finishTime = startTime
}
}
public var body: some View {
ZStack {
ActivityIndicator(isAnimating: .constant(true), style: .large)
.opacity(self.state.showIndeterminantProgress ? 1 : 0)
.frame(height: self.state.showIndeterminantProgress ? fullSize : 0)
ZStack {
ProgressView(progress: self.state.showProgressBar ? 1 : 0)
.frame(height: fullSize)
.animation(.linear(duration: self.duration))
}
.opacity(self.state.showProgressBar ? 1 : 0)
.frame(height: self.state.showProgressBar ? fullSize : 0)
Image(frameworkImage: "Checkmark").foregroundColor(Color.accentColor)
.opacity(self.state.showCompletion ? 1 : 0)
.scaleEffect(self.state.showCompletion ? 1.0 : 0.001)
.animation(.spring(dampingFraction: 0.5))
.frame(height: self.state.showCompletion ? fullSize : 0)
}
.accessibilityElement(children: .ignore)
.accessibility(label: Text(self.accessibilityLabel))
.accessibility(hidden: self.state == .hidden)
.onReceive(timer) { time in
let elapsed = CACurrentMediaTime() - self.startTime
self.progress = min(1.0, elapsed / self.duration)
if self.progress >= 1.0 {
self.timer.upstream.connect().cancel()
}
}
}
var accessibilityLabel: String {
switch self.state {
case .indeterminantProgress:
return LocalizedString("Progressing.", comment: "Accessibility label for ProgressIndicatorView when showIndeterminantProgress")
case .timedProgress:
return String(format: LocalizedString("%1$d percent complete.", comment: "Format string for progress accessibility label (1: duration in seconds)"), Int((self.progress * 100).rounded()))
case .completed:
return LocalizedString("Completed.", comment: "Accessibility label for ProgressIndicatorView when showIndeterminantProgress")
case .hidden:
return ""
}
}
}
struct ProgressIndicatorView_Previews: PreviewProvider {
static var previews: some View {
ProgressPreviewWrapper()
}
}
struct ProgressPreviewWrapper: View {
@State var setupState: ProgressIndicatorState = .hidden
@State private var modeIndex: Int = 0
var body: some View {
VStack {
Rectangle().frame(height: 1)
ProgressIndicatorView(state: setupState)
Rectangle().frame(height: 1)
Button(action: {
let finishTime = TimeInterval(10)
let modes: [ProgressIndicatorState] = [.indeterminantProgress, .timedProgress(finishTime: CACurrentMediaTime() + finishTime), .completed, .hidden]
self.setupState = modes[self.modeIndex]
self.modeIndex = (self.modeIndex + 1) % modes.count
}) {
Text("Switch Preview State")
}
Text(String(describing: self.setupState)).foregroundColor(Color.secondary).lineLimit(1)
}
.animation(.default)
.padding()
}
}