I'm trying to get the labels on 5 buttons in an HStack to appear one at a time with a 1 second delay between them appearing. In my code they all appear at once. Obviously I am not using sleep correctly. Not sure I should be using it at all. In other words I would like the "A" to appear, then one second later the "B" etc.
HStack (spacing: 25) {
ForEach(letters, id: \.self) { letter in
Button {
}
label : {
ZStack {
RoundedRectangle(cornerRadius: 15)
.fill(Color.blue.opacity(0.3))
.frame(width: 50, height: 50)
Text(letter)
.font(.largeTitle)
.foregroundColor(.black)
}
}
.task {
/// Delay of 1 second
try? await Task.sleep(nanoseconds: 1_000_000_000)
}
}
}
Currently, the task in your example is sleeping for 1 second, but then nothing happens afterwards. So at the moment, the task is serving no purpose.
To see an animation happen, there needs to be a change of state. A typical technique is to update a state variable that changes the view. This change can then be animated.
Here are a couple of ways of implementing a staggered animation. In both cases, the visibility of the letters is controlled by changing the opacity.
1. Trigger all animations together, then use .delay
to stagger the individual animations
@State private var isShowing = false
HStack (spacing: 25) {
ForEach(Array(letters.enumerated()), id: \.offset) { index, letter in
Button {} label: {
ZStack {
// ...
}
}
.opacity(isShowing ? 1 : 0)
.animation(.easeInOut.delay((Double(index + 1) * 1.0)), value: isShowing)
}
}
.onAppear {
isShowing = true
}
2. Use .task(id:)
to increment a counter
This approach is closer to what you had in your example:
@State private var nVisibleLetters = 0
HStack (spacing: 25) {
ForEach(Array(letters.enumerated()), id: \.offset) { index, letter in
Button {} label: {
ZStack {
// ...
}
}
.opacity(nVisibleLetters > index ? 1 : 0)
.animation(.easeInOut, value: nVisibleLetters)
}
}
.task(id: nVisibleLetters) {
try? await Task.sleep(for: .seconds(1))
if nVisibleLetters < letters.count {
nVisibleLetters += 1
}
}
The animation looks the same in both cases: