SyntaxStudy
Sign Up
Swift Animations and Custom Views
Swift Beginner 1 min read

Animations and Custom Views

SwiftUI animations are triggered by state changes. The withAnimation block or .animation modifier wraps changes that should be animated. Transitions control how views appear and disappear. Custom views are just structs with a body. Extracting sub-views is encouraged — SwiftUI is designed around small, composable view components. ViewModifiers can be extracted to reuse chains of modifiers. Canvas and GeometryReader enable advanced custom drawing and layout. PreferenceKey allows child views to communicate information up to ancestors.
Example
import SwiftUI

// Custom ViewModifier
struct CardStyle: ViewModifier {
    var color: Color = .white

    func body(content: Content) -> some View {
        content
            .padding()
            .background(color)
            .cornerRadius(12)
            .shadow(color: .black.opacity(0.1), radius: 4, x: 0, y: 2)
    }
}

extension View {
    func cardStyle(color: Color = .white) -> some View {
        modifier(CardStyle(color: color))
    }
}

// Animated loading button
struct LoadingButton: View {
    @State private var isLoading = false

    var body: some View {
        Button {
            withAnimation(.easeInOut(duration: 0.3)) { isLoading = true }
            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                withAnimation { isLoading = false }
            }
        } label: {
            Group {
                if isLoading {
                    ProgressView().tint(.white)
                } else {
                    Text("Submit")
                }
            }
            .frame(width: 120, height: 44)
        }
        .buttonStyle(.borderedProminent)
        .disabled(isLoading)
    }
}

// Custom drawing with Canvas
struct WaveView: View {
    @State private var phase: Double = 0

    var body: some View {
        Canvas { ctx, size in
            let path = Path { p in
                p.move(to: CGPoint(x: 0, y: size.height / 2))
                for x in stride(from: 0, through: size.width, by: 1) {
                    let y = sin(x / 20 + phase) * 30 + size.height / 2
                    p.addLine(to: CGPoint(x: x, y: y))
                }
            }
            ctx.stroke(path, with: .color(.blue), lineWidth: 2)
        }
        .onAppear {
            withAnimation(.linear(duration: 2).repeatForever(autoreverses: false)) {
                phase = .pi * 2
            }
        }
    }
}

This is the last lesson in this section.

Create a free account to earn a certificate