Leave a rating/review
At this point, you know plenty about creating animations with SwiftUI. But there is one last topic to cover: Creating beautiful animations driven by the user.
In this episode, you’ll add a drag gesture to the spinner control. That’ll give the user the ability to cancel the operation that the spinner represents
i.e. if the spinner shows up while a network request is taking place, swiping down on the spinner will cancel the network operation and hide the spinner view.
To achieve this, start by adding yet another state property to SpinnerView.
@State var isVisible = true
@State var currentOffset = CGSize.zero
let shootUp =
This will keep track of the user’s drag gesture translation whenever a gesture is in progress. Now, add a gesture modifier to your ZStack, with a DragGesture instance.
}
.gesture(
DragGesture()
)
.transition(shootUp)
Then, add an onChanged modifier to react to changes in the position of the drag gesture.
DragGesture()
.onChanged { gesture in
currentOffset = gesture.translation
}
)
During the gesture, you constantly update currentOffset with the current gesture translation. Next, let’s create a “complete” method.
}
func complete() {
}
func animate() {
Call it when you’re done with the timer, cut the code below that, and paste it in as the body of the complete method.
if iteration == 30 {
timer.invalidate()
complete()
}
func complete() {
completed = true
currentIndex = -1
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2)) {
withAnimation {
isVisible = false
}
}
}
And don’t allow for this code to run, if completed is already true.
func complete() {
guard !completed else { return }
completed = true
Next, add an onEnded modifier. And in its closure, in the case that the user swiped more than 150 points down, call complete().
.gesture(
DragGesture()
.onChanged { gesture in
currentOffset = gesture.translation
}
.onEnded { gesture in
if currentOffset.height > 150 {
complete()
}
}
)
Also, reset currentOffset, to animate the spinner back to its original place.
complete()
}
currentOffset = .zero
}
)
.transition(shootUp)
To wrap up, add three more modifiers to your ZStack, which will apply effects to the view during a drag gesture. An offset…
}
🟩.offset(currentOffset)
.gesture(
…and then a blur, for even more visual feedback.
.offset(currentOffset)
🟩.blur(radius: currentOffset == .zero ? 0 : 10)
.gesture(
…and lastly, a 1-second animation.
.blur(radius: currentOffset == .zero ? 0 : 10)
🟩.animation(.easeInOut(duration: 1), value: currentOffset)
.transition(shootUp)
Play the animation, in this completed state, and try dragging the spinner around and then releasing it. If you drag upwards or sideways, the spinner animates back to its original place.
But if you drag it down far enough, that kicks off the completion animation right away.