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()
}