Skip to content

Commit 6c84091

Browse files
authored
Update mastering-progressview-swiftui.md
1 parent 31fee1f commit 6c84091

File tree

1 file changed

+105
-124
lines changed

1 file changed

+105
-124
lines changed
Lines changed: 105 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -1,199 +1,180 @@
1-
В процессе написания приложения, у нас появляются методы, которые выполняют фоновую работу, будь то работа с сетью или обработка данных. Для того, чтобы обозначить такую работу, нам поможет `ProgressView` — вью, которое показывает ход выполнения задачи/работы.
2-
1+
Чтобы обозначить фоновую работу в приложении используют `ProgressView`.
32

43
## Неопределенный прогресс
54

6-
Для того, чтобы создать индикатор загрузки, поместим `ProgressView()` в нашу вью:
5+
Добавим `ProgressView()`:
76

87
```swift
98
struct ContentView: View {
10-
var body: some View {
11-
VStack(spacing: 40) {
12-
ProgressView()
13-
Divider()
14-
ProgressView("Loading")
15-
.tint(.pink)
16-
}
9+
10+
var body: some View {
11+
VStack(spacing: 40) {
12+
ProgressView()
13+
Divider()
14+
ProgressView("Loading")
15+
.tint(.pink)
16+
}
17+
}
1718
}
1819
```
1920

21+
[Indeterminate Activity Indicator](https://cdn.ivanvorobei.by/websites/sparrowcode.io/mastering-progressview-swiftui/indeterminate_activity_indicator.mov)
2022

21-
[Indeterminate activity indicator](https://cdn.ivanvorobei.by/websites/sparrowcode.io/mastering-progressview-swiftui/indeterminate_activity_indicator.mov)
22-
23-
По умолчанию SwiftUI определяет вращающийся бар загрузки (спиннер), которым обозначают некоторую работу в фоне.
24-
Обратите внимание, что с помощью модификатора `.tint()` можно изменить цвет бара.
25-
Теперь посмотрим на другой способ инициализирования.
26-
23+
По умолчанию `SwiftUI` определяет вращающийся бар загрузки (спиннер). Модификатор `.tint()` меняет цвет бара.
2724

2825
## Определенный прогресс
2926

30-
В отличии от неопределенного, мы можем показать прогресс, который имеет явный индикатор.
31-
Для этого инициализируем вью способом ниже:
27+
Используем явный индикатор - инициализируем вью:
3228

3329
```swift
3430
struct ContentView: View {
35-
let totalProgress: Double = 100
36-
@State private var progress = 0.0
37-
38-
var body: some View {
39-
VStack(spacing: 40) {
40-
currentTextProgress
41-
42-
ProgressView(value: progress, total: totalProgress)
43-
.padding(.horizontal, 40)
44-
45-
loadResetButtons
31+
32+
let totalProgress: Double = 100
33+
@State private var progress = 0.0
34+
35+
var body: some View {
36+
VStack(spacing: 40) {
37+
currentTextProgress
38+
39+
ProgressView(value: progress, total: totalProgress)
40+
.padding(.horizontal, 40)
41+
42+
loadResetButtons
43+
}
4644
}
47-
}
4845
}
4946

5047
extension ContentView {
51-
private var currentTextProgress: Text {
52-
switch progress {
48+
49+
private var currentTextProgress: Text {
50+
switch progress {
5351
case 5..<totalProgress: return Text("Current progress: \(Int(progress))%")
5452
case totalProgress...: return Text("Loading complete")
5553
default: return Text("Start loading")
56-
}
54+
}
5755
}
5856

59-
private var loadResetButtons: some View {
60-
HStack(spacing: 20) {
61-
Button("Load more") {
62-
withAnimation { progress += Double.random(in: 5...20) }
63-
}
64-
.disabled(!progress.isLessThanOrEqualTo(totalProgress))
65-
66-
Button(role: .destructive) {
67-
progress = 0
68-
} label: {
69-
Text("Reset")
70-
}
71-
.tint(.red)
72-
.disabled(progress.isZero)
57+
private var loadResetButtons: some View {
58+
HStack(spacing: 20) {
59+
Button("Load more") {
60+
withAnimation { progress += Double.random(in: 5...20) }
61+
}
62+
.disabled(!progress.isLessThanOrEqualTo(totalProgress))
63+
64+
Button(role: .destructive) {
65+
progress = 0
66+
} label: {
67+
Text("Reset")
68+
}
69+
.tint(.red)
70+
.disabled(progress.isZero)
71+
}
72+
.buttonStyle(.bordered)
7373
}
74-
.buttonStyle(.bordered)
75-
}
7674
}
7775
```
7876

79-
[Determinate activity indicator](https://cdn.ivanvorobei.by/websites/sparrowcode.io/mastering-progressview-swiftui/determinate_activity_indicator.mov)
77+
[Determinate Activity Indicator](https://cdn.ivanvorobei.by/websites/sparrowcode.io/mastering-progressview-swiftui/determinate_activity_indicator.mov)
8078

79+
По нажатию на `Load more` начинается загрузка. Текст показывает текущий прогресс, а кнопка `Reset` станет доступной для нажатия и сброса. Когда загрузка закончится, текст на экране сообщит об этом. Кнопка `Load more` станет неактивной.
8180

82-
По нажатию кнопки `Load more` начинается загрузка, текст показывает текущий прогресс, а кнопка `Reset` становится доступной для нажатия и сброса прогресса. По достижению загрузки, текст на экране сообщает о завершении загрузки и кнопка `Load more` становится неактивной.
83-
84-
Для симуляции прогресса, я покажу еще один пример с таймером.
81+
Сделаем симуляцию прогресса c таймером:
8582

8683
```swift
8784
// filename: TimerProgressView.swift
8885

8986
struct TimerProgressView: View {
90-
let timer = Timer
91-
.publish(every: 0.05, on: .main, in: .common)
92-
.autoconnect()
93-
94-
let downloadTotal: Double = 100
95-
@State private var progress: Double = 0
9687

97-
var body: some View {
98-
VStack(spacing: 40) {
99-
Text("Downloading: \(Int(progress))%")
100-
101-
ProgressView(value: progress, total: downloadTotal)
102-
.tint(progress < 50 ? .pink : .green)
103-
.padding(.horizontal)
104-
.onReceive(timer) { _ in
105-
if progress < downloadTotal { progress += 1 }
106-
}
88+
let timer = Timer
89+
.publish(every: 0.05, on: .main, in: .common)
90+
.autoconnect()
91+
92+
let downloadTotal: Double = 100
93+
@State private var progress: Double = 0
94+
95+
var body: some View {
96+
VStack(spacing: 40) {
97+
Text("Downloading: \(Int(progress))%")
98+
99+
ProgressView(value: progress, total: downloadTotal)
100+
.tint(progress < 50 ? .pink : .green)
101+
.padding(.horizontal)
102+
.onReceive(timer) { _ in
103+
if progress < downloadTotal { progress += 1 }
104+
}
105+
}
107106
}
108-
}
109107
}
110108
```
111109

112-
[Timer progress](https://cdn.ivanvorobei.by/websites/sparrowcode.io/mastering-progressview-swiftui/timer_progress.mov)
113-
110+
[Timer Progress](https://cdn.ivanvorobei.by/websites/sparrowcode.io/mastering-progressview-swiftui/timer_progress.mov)
114111

115-
В данном примере, мы вызываем событие многократно, используя таймер.
116-
Код для создания таймера выглядит так:
112+
Событие вызывается несколько раз при помощи таймера. Код таймера выглядит так:
117113

118114
```swift
119115
let timer = Timer.publish(every: 0.05, on: .main, in: .common).autoconnect()
120116
```
121117

122-
Поясню некоторые моменты:
123-
124-
1. Таймер срабатывает каждые 0.05 секунд, что равняется 50 миллисекунд.
125-
2. Таймер должен работать в главном потоке.
126-
3. Таймер должен работать в общем цикле(common run loop). Run loop позволяет обрабатывать работающий
127-
код, когда пользователь делает что-либо, например нажимает на кнопку.
128-
4. Таймер подключается немедленно и начинается сразу отчитывать время.
118+
Таймер срабатывает каждые 0.05 секунд (50 миллисекунд). Таймер должен работать в главном потоке и общем цикле (common run loop). Run loop позволяет обрабатывать код, когда пользователь делает что-либо (нажимает кнопку). Таймер начинает отсчитывтаь время моментально.
129119

130120
Когда `progress` достигнет `downloadTotal` значения, таймер остановится.
131121
При достижении 50% загрузки, индикатор меняет цвет на зеленый.
132122

133-
При таком объявлении, `ProgressView` выглядит как полоса загрузки, которая заполняется слева направо.
134-
Так можно показать пользователю, что загрузка данных выполняется в зависимости от размера файла.
135-
123+
`ProgressView` выглядит как полоса загрузки, которая заполняется слева направо.
124+
Так показываем пользователю, что прогресс загрузки зависит от размера файла.
136125

137-
Описание метода `publish` доступно в [документации Apple](https://developer.apple.com/documentation/foundation/timer/3329589-publish).
138-
139-
Больше инициализаторов можно найти в документации Xcode или [на сайте](https://developer.apple.com/documentation/swiftui/progressview).
126+
Описание метода `publish` доступно в [документации Apple](https://developer.apple.com/documentation/foundation/timer/3329589-publish). Больше инициализаторов можно найти в документации Xcode или [на сайте](https://developer.apple.com/documentation/swiftui/progressview).
140127

141128
![Documentation SwiftUI ProgressView](https://cdn.ivanvorobei.by/websites/sparrowcode.io/mastering-progressview-swiftui/progressview_init.png)
142129

143-
144130
## Дизайн
145131

146-
147-
SwiftUI предоставляет протокол `ProgressViewStyle`, который позволяет создавать собственный дизайн для `ProgressView`.
148-
Например, вы можете настроить вид и взаимодействие прогресса, создав стиль, который наследуется от протокола `ProgressViewStyle`.
149-
150-
151-
Для этого объявим структуру `RoundedProgressViewStyle`, которая наследуется от протокола `ProgressViewStyle` и содержит метод `makeBody()`, принимающий параметр конфигурации для нашего стиля.
132+
Собственный дизайн для `ProgressView` созадется с помощью протокола `ProgressViewStyle`, нужно наследоваться от него. Объявим структуру `RoundedProgressViewStyle`, которая содержит метод `makeBody()` и принимает параметр конфигурации для стиля:
152133

153134
```swift
154135
struct RoundedProgressViewStyle: ProgressViewStyle {
155-
let color: Color
156-
157-
func makeBody(configuration: Configuration) -> some View {
158-
let fractionCompleted = configuration.fractionCompleted ?? 0
159136

160-
RoundedRectangle(cornerRadius: 18)
161-
.frame(width: CGFloat(fractionCompleted) * 200, height: 22)
162-
.foregroundColor(color)
163-
.padding(.horizontal)
164-
}
137+
let color: Color
138+
139+
func makeBody(configuration: Configuration) -> some View {
140+
let fractionCompleted = configuration.fractionCompleted ?? 0
141+
142+
RoundedRectangle(cornerRadius: 18)
143+
.frame(width: CGFloat(fractionCompleted) * 200, height: 22)
144+
.foregroundColor(color)
145+
.padding(.horizontal)
146+
}
165147
}
166148
```
167149

168-
Далее вернемся к `TimerProgressView.swift` и передадим `RoundedProgressViewStyle(color: .cyan)` в модификатор `.progressViewStyle()`.
169-
170-
Теперь код выглядит так:
150+
Возвращаемся к `TimerProgressView.swift` и передадим `RoundedProgressViewStyle(color: .cyan)` в модификатор `.progressViewStyle()`. Теперь код выглядит так:
171151

172152
```swift
173153
struct TimerProgressView: View {
174-
let timer = Timer
175-
.publish(every: 0.05, on: .main, in: .common)
176-
.autoconnect()
177-
178-
let downloadTotal: Double = 100
179-
@State private var progress: Double = 0
180154

181-
var body: some View {
182-
VStack(spacing: 40) {
183-
Text("Downloading: \(Int(progress))%")
184-
185-
ProgressView(value: progress, total: downloadTotal)
186-
.onReceive(timer) { _ in
187-
if progress < downloadTotal { progress += 1 }
155+
let timer = Timer
156+
.publish(every: 0.05, on: .main, in: .common)
157+
.autoconnect()
158+
159+
let downloadTotal: Double = 100
160+
@State private var progress: Double = 0
161+
162+
var body: some View {
163+
VStack(spacing: 40) {
164+
Text("Downloading: \(Int(progress))%")
165+
166+
ProgressView(value: progress, total: downloadTotal)
167+
.onReceive(timer) { _ in
168+
if progress < downloadTotal { progress += 1 }
169+
}
170+
.progressViewStyle(
171+
RoundedProgressViewStyle(color: .cyan)
172+
)
188173
}
189-
.progressViewStyle(
190-
RoundedProgressViewStyle(color: .cyan)
191-
)
192174
}
193-
}
194175
}
195176
```
196177

197-
Обратите внимание, что прогресс начинается не слева направо, а с середины в противоположные стороны.
178+
Прогресс начинается не слева направо, а с середины в противоположные стороны.
198179

199180
[RoundedProgressViewStyle](mov)

0 commit comments

Comments
 (0)