186 |
187 |  |
188 |  |
189 |
190 |
191 | | .asyncButtonStyle(.overlay) |
192 | .asyncButtonStyle(.pulse) |
193 |
194 |
195 |  |
196 |  |
197 |
198 |
199 | | .asyncButtonStyle(.leading) |
200 | .asyncButtonStyle(.trailing) |
201 |
202 |
203 | | No preview |
204 | |
205 |
206 |
207 | | .asyncButtonStyle(.symbolEffect(.bounce)) |
208 | |
209 |
210 |
211 |
212 | You can disable this behavior by passing `.none` to `asyncButtonStyle`
213 | ```swift
214 | AsyncButton {
215 | try await doSomethingThatTakeTime()
216 | } label {
217 | Text("Do something")
218 | }
219 | .asyncButtonStyle(.none)
220 | ```
221 |
222 | You can also build your own customization by implementing `AsyncButtonStyle` protocol.
223 |
224 | Just like `ThrowableButtonStyle`, `AsyncButtonStyle` allows you to implement either `makeLabel`, `makeButton` or both to alter the button look and behavior while loading is in progress.
225 |
226 | ### External triggering
227 |
228 | You might need to trigger the behavior behind a button with specific user actions, like when pressing the "Send" key on the virtual keyboard.
229 |
230 | Therefore, to get free animated progress and errors behavior on your button, you can't just start the action of the button by yourself. You need the button to start it.
231 |
232 | To do so, you need to set a unique identifier to your button:
233 |
234 | ```swift
235 | enum LoginViewButton: Hashable {
236 | case login
237 | }
238 |
239 | struct ContentView: View {
240 | var body: some View {
241 | AsyncButton(id: LoginViewButton.login) {
242 | try await login()
243 | } label: {
244 | Text("Login")
245 | }
246 | }
247 | }
248 | ```
249 |
250 | And from any view, access the triggerButton environment:
251 |
252 | ```swift
253 | struct ContentView: View {
254 | @Environment(\.triggerButton)
255 | private var triggerButton
256 |
257 | ...
258 |
259 | func performLogin() {
260 | triggerButton(LoginViewButton.login)
261 | }
262 | }
263 | ```
264 |
265 | Note that:
266 | - The button **Must be on screen** to trigger it using this method.
267 | - If the triggered button is disabled, calling triggerButton will have no effect
268 | - If a task has already started on the triggered button, calling triggerButton will have no effect
269 |
270 | ### Deterministic progress
271 |
272 | AsyncButton supports progress reporting:
273 |
274 | ```swift
275 | AsyncButton(progress: .discrete(totalUnitCount: files.count)) { progress in
276 | for file in files {
277 | try await file.doExpensiveComputation()
278 | progress.completedUnitCount += 1
279 | }
280 | } label: {
281 | Text("Process")
282 | }
283 | .buttonStyle(.borderedProminent)
284 | .buttonBorderShape(.roundedRectangle)
285 | ```
286 |
287 | `AsyncButtonStyle` now also supports determinate progress as well, responding to `configuration.fractionCompleted: Double?` property:
288 |
289 | ```swift
290 | AsyncButton(progress: .discrete(totalUnitCount: files.count)) { progress in
291 | for file in files {
292 | try await file.doExpensiveComputation()
293 | progress.completedUnitCount += 1
294 | }
295 | } label: {
296 | Text("Process")
297 | }
298 | .buttonStyle(.borderedProminent)
299 | .buttonBorderShape(.roundedRectangle)
300 | .asyncButtonStyle(.trailing)
301 | ```
302 |
303 |