In the view, there are two buttons that display a text prompt when clicked, and the display of the text is controlled by the @State var tipText: String?
. I have written an asynchronous function using the .task(id:priority:_:)
modifier that shows the text when the user clicks a button, and hides it automatically after three seconds.
struct SwiftUIView: View {
@State var tipText: String?
var body: some View {
ZStack {
VStack {
HStack {
Button("Red") {
tipText = "Red"
}
.tint(.red)
.controlSize(.large)
.buttonStyle(.borderedProminent)
Button("Blue") {
tipText = "Blue"
}
.tint(.blue)
.controlSize(.large)
.buttonStyle(.borderedProminent)
}
Spacer()
}
.padding()
// Shows the text when the user clicks a button
if let tipText = tipText {
Text(
"""
You have just clicked the (tipText) button,
this prompt will disappear in 3 seconds.
"""
)
.padding()
.background(.ultraThickMaterial)
.cornerRadius(10)
// Close the text prompt after three seconds.
.task(id: tipText) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
self.tipText = nil
}
}
}
}
}
}
However, if the user quickly clicks back and forth between the two buttons, the text will disappear early. I suspect that this is because the earlier execution of the asynchronous function caused the text to be hidden prematurely.
My expectation is that regardless of how many times the user clicks continuously, the text must wait for three seconds after I stop clicking before disappearing. Is there a better way to implement this?
2
Answers
The new Concurency does not mix with
DispatchQueue
.Replace
With
https://developer.apple.com/documentation/swift/task/sleep(for🙂
Or the nanosecond version that is available for earlier versions.
https://developer.apple.com/documentation/swift/task/sleep(nanoseconds🙂
.task
is called on appear and is cancelled/restarted every time theid
param changes so you need logic to handle those cases. Also you’ll need a counter e.g.