In this tutorial, we'll walk through a SwiftUI code that creates an interactive cat animation. The app displays three overlapping cat icons that move together when the user taps anywhere on the screen. Let's break down the code and understand how it works.
Setting Up the View
First, we import SwiftUI and define our main ContentView struct:
import SwiftUI
struct ContentView: View {
@State private var position: CGPoint = CGPoint(x: UIScreen.main.bounds.width / 2, y: UIScreen.main.bounds.height / 2)
// ... (we'll cover the rest of the code later)
}
We use a @State property wrapper to create a mutable position variable. This will store the current position of our cats, initially set to the center of the screen.
Handling Safe Area
To ensure our content doesn't overlap with the device's safe areas, we calculate the bottom safe area inset:
var safeAreaBottom: CGFloat {
let scenes = UIApplication.shared.connectedScenes
let windowScene = scenes.first as? UIWindowScene
let window = windowScene?.windows.first
return window?.safeAreaInsets.bottom ?? 0
}
This computed property retrieves the bottom safe area inset, which we'll use later to adjust our layout.
Building the User Interface
Now, let's look at the body property where we construct our UI:
var body: some View {
GeometryReader { geometry in
VStack(alignment: .leading, spacing: 0) {
// Header
VStack(alignment: .leading) {
Text("Tap anywhere to move the cats")
.font(.headline)
.padding()
}
.frame(maxWidth: .infinity, alignment: .topLeading)
// Cat stack
ZStack {
catView(scale: 0.9, opacity: 0.3)
catView(scale: 0.95, opacity: 0.5)
catView(scale: 1.0, opacity: 1.0)
}
.position(position)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(hex: 0xe4f6ff).ignoresSafeArea())
.contentShape(Rectangle())
.onTapGesture { location in
position = location
}
}
.animation(.spring(), value: position)
}
We use a GeometryReader to access the size of our view. Inside, we have a VStack containing:
A header with instructions
An overlay with additional text
A ZStack of three cat views
The cat views are positioned using the position state variable. We apply a light blue background color and add a tap gesture to update the cats' position.
Creating the Cat View
The catView function creates a single cat icon:
func catView(scale: CGFloat, opacity: Double) -> some View {
Image(systemName: "cat.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200, height: 200)
.foregroundColor(.orange)
.shadow(color: .black.opacity(0.16), radius: 4, x: 0, y: 2)
.scaleEffect(scale)
.opacity(opacity)
}
This function takes a scale and opacity parameter, allowing us to create three slightly different cat views for a layered effect.
Custom Color Extension
To use a hex color value for our background, we add an extension to the Color struct:
extension Color {
init(hex: Int, alpha: Double = 1.0) {
let red = Double((hex & 0xff0000) >> 16) / 255.0
let green = Double((hex & 0xff00) >> 8) / 255.0
let blue = Double((hex & 0xff) >> 0) / 255.0
self.init(.sRGB, red: red, green: green, blue: blue, opacity: alpha)
}
}
This allows us to create colors using hexadecimal values, like Color(hex: 0xe4f6ff).
Apply the save area
.onTapGesture { location in
// position = location
let adjustedY = min(location.y, geometry.size.height - safeAreaBottom)
position = CGPoint(x: location.x, y: adjustedY)
}
Preview
Finally, we add a preview provider to see our view in Xcode's canvas:
#Preview {
ContentView()
}