Instantly share code, notes, and snippets.
Created
October 17, 2022 21:00
-
Star
(4)
4
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save lukepistrol/67104dbe02f58d2b3c47495945828a57 to your computer and use it in GitHub Desktop.
A SwiftUI Button that can perform asyncronous tasks.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
/// A control that initiates an action asyncronously. | |
/// | |
/// You create a button by providing an action and a label. | |
/// The action is either a method or closure property that | |
/// does something when a user clicks or taps the button. | |
/// The label is a view that describes the button’s action — | |
/// for example, by showing text, an icon, or both: | |
/// | |
/// ```swift | |
/// AsyncButton(action: signIn) { | |
/// Text("Sign In") | |
/// } | |
/// ``` | |
/// | |
/// For the common case of text-only labels, you can use | |
/// the convenience initializer that takes a title string or | |
/// LocalizedStringKey as its first parameter, instead of | |
/// a trailing closure: | |
/// | |
/// ```swift | |
/// AsyncButton("Sign In", action: signIn) | |
/// ``` | |
/// | |
/// How the user activates the button varies by platform: | |
/// - In iOS and watchOS, the user taps the button. | |
/// - In macOS, the user clicks the button. | |
/// - In tvOS, the user presses “select” on an external remote, | |
/// like the Siri Remote, while focusing on the button. | |
/// | |
/// The appearance of the button depends on factors like | |
/// where you place it, whether you assign it a role, and | |
/// how you style it. | |
struct AsyncButton<Label: View>: View { | |
var action: () async -> Void | |
@ViewBuilder var label: () -> Label | |
var role: ButtonRole? | |
/// Creates an AsyncButton with a specified role, | |
/// that displays a custom label. | |
/// | |
/// - Parameters: | |
/// - role: An optional semantic role that describes | |
/// the button. A value of nil means that the button | |
/// doesn’t have an assigned role. | |
/// - action: The action to perform when the user | |
/// interacts with the button. | |
/// - label: A view that describes the purpose of | |
/// the button’s action. | |
init( | |
role: ButtonRole? = nil, | |
action: @escaping () async -> Void, | |
label: @escaping () -> Label | |
) { | |
self.action = action | |
self.label = label | |
self.role = role | |
} | |
@State private var isPerformingTask = false | |
var body: some View { | |
Button(role: role) { | |
isPerformingTask = true | |
Task { | |
await action() | |
isPerformingTask = false | |
} | |
} label: { | |
label() | |
} | |
.disabled(isPerformingTask) | |
} | |
} | |
extension AsyncButton where Label == Text { | |
/// Creates an AsyncButton with a specified role, | |
/// that displays a string as a label. | |
/// | |
/// This init creates a Text automatically to display the given | |
/// string. | |
/// | |
/// - Parameters: | |
/// - title: A String to display as a label. | |
/// - role: An optional semantic role that describes | |
/// the button. A value of nil means that the button | |
/// doesn’t have an assigned role. | |
/// - action: The action to perform when the user | |
/// interacts with the button. | |
init<S>( | |
_ title: S, | |
role: ButtonRole? = nil, | |
action: @escaping () async -> Void | |
) where S: StringProtocol { | |
self.init(role: role, action: action) { | |
Text(title) | |
} | |
} | |
/// Creates an AsyncButton with a specified role, | |
/// that displays a string as a label. | |
/// | |
/// This init creates a Text automatically to display the string | |
/// for the given localized string key. | |
/// | |
/// - Parameters: | |
/// - titleKey: A localized string key for displaying | |
/// a string as a label. | |
/// - role: An optional semantic role that describes | |
/// the button. A value of nil means that the button | |
/// doesn’t have an assigned role. | |
/// - action: The action to perform when the user | |
/// interacts with the button. | |
init( | |
_ titleKey: LocalizedStringKey, | |
role: ButtonRole? = nil, | |
action: @escaping () async -> Void | |
) { | |
self.init(role: role, action: action) { | |
Text(titleKey) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment