SwiftUI Animation
Basic Animations in SwiftUI can easily be created using state variables and adjusting them within withAnimation blocks. In this example a view's offset is controlled using a drag gesture. When dragging exceeds a threshold, a new animation is started.
import SwiftUI
struct SampleAnimationView: View {
@State var taskQuestion: String = "1 + 1"
@State var taskIndex: Int = 1
@State var taskDragActive: Bool = false
@State var taskDragEndDone: Bool = false
let actionThreshold: CGFloat = -100.0
@State var actionValue: CGFloat = 0.0
@State var opacityValue: CGFloat = 1.0
@State var offsetValue: CGFloat = 0.0
var body: some View {
VStack(spacing: 0) {
VStack {
HStack {
Text(taskQuestion)
.frame(maxWidth: .infinity)
}
.font(.system(size: 40))
.opacity(opacityValue)
.offset(x: offsetValue, y: 0)
}
.padding(5)
.frame(maxWidth: .infinity)
.background(content: {
RoundedRectangle(cornerRadius: 16)
.fill(Color.white)
.opacity(0.30)
})
.gesture(
DragGesture(
minimumDistance: 5.0,
coordinateSpace: .global)
.onChanged({ value in
if !taskDragActive {
taskDragActive = true
taskDragEndDone = false
}
actionValue = value.translation.width
opacityValue = max(1.0 - abs(actionValue) * 0.01, 0.0)
offsetValue = actionValue
if actionValue < actionThreshold {
if !taskDragEndDone {
taskDragEndDone = true
}
}
})
.onEnded({ value in
if actionValue >= actionThreshold {
taskDragActive = false
withAnimation {
actionValue = 0
opacityValue = 1.0
offsetValue = 0
}
} else if taskDragEndDone {
animateNextTask()
animateGestureForTask()
}
})
)
.padding(.vertical, 8)
.foregroundStyle(.primary)
VStack {
Text("Swipe for a new task.")
.foregroundStyle(.secondary)
.font(.system(size: 14))
.multilineTextAlignment(.center)
}
}
.padding(30)
.background {
Image("background")
.resizable()
.scaledToFill()
.padding(-80)
.blur(radius: 150.0)
.brightness(0.3)
}
}
private func animateGestureForTask() {
opacityValue = 0.0
offsetValue = 150.0
withAnimation(.spring(duration: 0.8, bounce: 0.1)) {
offsetValue = 0.0
}
withAnimation(.easeOut(duration: 1.8)) {
opacityValue = 1.0
}
}
private func animateNextTask() {
taskIndex += 1
taskQuestion = "\(taskIndex) + \(taskIndex)"
}
}
#Preview {
SampleAnimationView()
}