Skip to main content

Alessandros's Blog - Build with freedom. Share with purpose

8 aout 2025 - Suite des Animations SwiftUI : quand l’interface prend vie

Voici ce que j’ai vu au niveau des animations ce vendredi. je poste cet article le dimanche car j’ai eu la flemme de le faire vendredi.

# Apprentissages du jour : Les animations SwiftUI

## Animation implicite : le modificateur .animation()

La première approche que j’ai découverte, c’est l’animation implicite. SwiftUI surveille automatiquement les changements d’état et les anime :

struct ContentView: View {
    @State private var enabled = false
    
    var body: some View {
        Button("Tap Me"){
            enabled.toggle()
        }
        .frame(width: 200, height: 200)
        .background(enabled ? .blue : .red)
        .foregroundStyle(.white)
        .animation(.default, value: enabled)
        .clipShape(.rect(cornerRadius: enabled ? 60 : 0))
        .animation(.spring(duration: 1, bounce: 0.6), value: enabled)
    }
}

Astuce importante : On peut appliquer différentes animations à différentes propriétés du même élément !

## Gestes et animations : le drag interactif

L’une des choses les plus impressionnantes que j’ai testées aujourd’hui, ce sont les gestes de drag combinés aux animations :

struct ContentView: View {
    @State private var dragAmount = CGSize.zero // CGSize du framework CoreGraphics
    
    var body: some View {
        LinearGradient(colors: [.yellow, .red], startPoint: .topLeading, endPoint: .bottomTrailing)
            .frame(width: 300, height: 200)
            .clipShape(.rect(cornerRadius: 10))
            .offset(dragAmount)
            .gesture(
                DragGesture()
                    .onChanged { dragAmount = $0.translation }
                    .onEnded { _ in
                        withAnimation(.bouncy) {
                            dragAmount = .zero
                        }
                    }
            )
    }
}

## Animation séquentielle : l’effet lettres

L’un des exemples les plus spectaculaires que j’ai réalisé aujourd’hui, c’est l’animation de lettres individuelles :

struct ContentView: View {
    let letters = Array("Hello SwiftUI")
    
    @State private var enabled = false
    @State private var dragAmount = CGSize.zero
    
    var body: some View {
        HStack (spacing: 0){
            ForEach(0..<letters.count, id: \.self) { num in
                Text(String(letters[num]))
                    .padding(5)
                    .font(.title)
                    .background(enabled ? .blue : .red)
                    .offset(dragAmount)
                    .animation(.linear.delay(Double(num) / 20), value: dragAmount)
            }
        }
        .gesture(
            DragGesture()
                .onChanged { dragAmount = $0.translation }
                .onEnded { _ in
                    dragAmount = .zero
                    enabled.toggle()
                }
            )
    }
}

Chaque lettre s’anime avec un délai différent, créant un effet de vague.

## Transitions : apparition et disparition

Les transitions permettent de contrôler comment les éléments apparaissent et disparaissent :

struct ContentView: View {
    @State private var isShowingRed = false
    
    var body: some View {
         VStack {
             Button("Tap Me") {
                 withAnimation {
                     isShowingRed.toggle()
                 }
             }
             
             if isShowingRed {
                 Rectangle()
                     .fill(.red)
                     .frame(width: 200, height: 200)
                     .transition(.asymmetric(insertion: .push(from: .bottom), removal: .push(from: .top)))
             }
        }
    }
}

L’approche asymétrique permet d’avoir des animations différentes à l’entrée et à la sortie.

## ViewModifier personnalisé : créer ses propres transitions

Le concept le plus avancé que j’ai découvert aujourd’hui, c’est la création de transitions personnalisées avec des ViewModifiers :

struct CornerRotateModifier: ViewModifier {
    let amount: Double
    let anchor: UnitPoint
    
    func body(content: Content) -> some View {
        content
            .rotationEffect(.degrees(amount), anchor: anchor)
            .clipped()
    }
}

extension AnyTransition {
    static var pivot: AnyTransition {
        .modifier(
            active: CornerRotateModifier(amount: -90, anchor: .topLeading),
            identity: CornerRotateModifier(amount: 0, anchor: .topLeading))
    }
}

Cette approche permet de créer des effets uniques personnalisés pour tes applications !

# Ma création personnelle : l’animation “WRONG!”

Je suis assez fier de cette petite animation que j’ai créée pour tester mes connaissances :

struct ContentView: View {
    @State private var isWrong = true
    
    var body: some View {
        ZStack {
            Capsule()
                .frame(width: 100, height: 30)
            Text("WRONG !")
                .foregroundStyle(.white)
        }
        .offset(x: isWrong ? 10: 0)
        .onTapGesture {
            isWrong = false
            withAnimation(.bouncy(duration: 0.2, extraBounce: 0.6).repeatCount(1)){
                isWrong = true
            }
        }
    }
}

L’idée : quand on tape sur le bouton, le bouton vibre pour indiquer une erreur. L’animation .bouncy avec extraBounce donne vraiment l’impression d’un objet qui vibre de frustration !

Alessandro