Ana Sayfa / Blog / SwiftUI animation örüntüleri: implicit, explicit, matchedGeometryEffect

SwiftUI animation örüntüleri: implicit, explicit, matchedGeometryEffect

SwiftUI animasyonu 3 farklı seviyede çalışıyor. Her birinin ne zaman doğru olduğunu gerçek projelerden örneklerle.

SwiftUI animation API’si güçlü ama kafa karıştırıcı. .animation() modifier’ı, withAnimation { } block’u, matchedGeometryEffect. Ne zaman hangisini kullanmalı?

12 iOS uygulamamda farklı animation pattern’leri kullandım. Her birinin kendi use case’i var. Bu yazıda pratik örneklerle anlatacağım.

Implicit animation

En basit ve en yaygın. State değişikliği otomatik animate ediyor.

struct ContentView: View {
    @State private var isExpanded = false
    
    var body: some View {
        VStack {
            Rectangle()
                .frame(width: isExpanded ? 300 : 100, height: 100)
                .animation(.easeInOut, value: isExpanded)
            
            Button("Toggle") { isExpanded.toggle() }
        }
    }
}

.animation(_:value:) modifier’ı. isExpanded değiştiğinde Rectangle’ın frame’ini smooth animate ediyor.

Ne zaman kullanılır:
– Basit property değişiklikleri (frame, opacity, color)
– UI component’in kendi davranışı (toggle, expand)
– Declarative, component-level animation

Dikkat: value parameter’ı kritik. Olmazsa view’ın her change’inde animate oluyor.

Explicit animation

withAnimation block’u state change’i animate ediyor.

@State private var offset: CGFloat = 0

Button("Move") {
    withAnimation(.spring()) {
        offset = 200
    }
}

Circle()
    .offset(x: offset)

Not: .animation() modifier yok. withAnimation block’u içindeki state change’ler animate oluyor.

Avantaj: Specific action’ın animation’ını kontrol ediyorsun. Başka state change’ler animate olmuyor.

Ne zaman kullanılır:
– User interaction’ın reaction’ı (button tap, swipe)
– Complex state machine transitions
– Network response’dan sonra UI update

matchedGeometryEffect

En güçlü ve en karmaşık. İki farklı view arasında shared element transition.

struct ContentView: View {
    @State private var selected: Bool = false
    @Namespace private var namespace
    
    var body: some View {
        if selected {
            FullscreenView(namespace: namespace)
        } else {
            ThumbnailView(namespace: namespace)
        }
    }
}

struct ThumbnailView: View {
    let namespace: Namespace.ID
    
    var body: some View {
        Image("photo")
            .matchedGeometryEffect(id: "photo", in: namespace)
            .frame(width: 100, height: 100)
    }
}

struct FullscreenView: View {
    let namespace: Namespace.ID
    
    var body: some View {
        Image("photo")
            .matchedGeometryEffect(id: "photo", in: namespace)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
    }
}

Kullanıcı thumbnail’e tıklıyor, image smoothly fullscreen’e büyüyor. iOS Photos app style.

Ne zaman kullanılır:
– Photo gallery → detail transition
– List item → detail view
– Tab bar → full screen
– Any “hero transition” pattern

Dentii’de brushing session thumbnail’den detail view’a transition bu pattern’le. User experience dramatically iyi.

Animation curves

SwiftUI built-in curves:

  • .linear: constant speed
  • .easeIn: slow start, fast end
  • .easeOut: fast start, slow end
  • .easeInOut: slow-fast-slow
  • .spring(): physical spring simulation
  • .interactiveSpring(): user-controllable spring

Custom timing:

.animation(.easeInOut(duration: 0.5), value: state)
.animation(.spring(response: 0.4, dampingFraction: 0.6), value: state)

Spring animations natural hissediyor. Çoğu interaction için default tercih.

Transitions

View’ın girişi/çıkışı için transition:

if show {
    Text("Hello")
        .transition(.slide)
}

Built-in: .slide, .opacity, .move(edge: .top), .scale.

AsymmetricTransition:

.transition(.asymmetric(
    insertion: .move(edge: .top),
    removal: .move(edge: .bottom)
))

Entry/exit farklı animation. Pattern’i toast notification için kullanıyorum.

Performance considerations

Animation pahalı. Her frame GPU work.

Golden rules:

  1. Transform only. offset(), scaleEffect(), rotationEffect() GPU-accelerated.
  2. Avoid layout changes. frame() değiştirmek layout recomputation tetikliyor.
  3. Opacity OK. Alpha change GPU’da ucuz.
  4. Complex path animation expensive. Basit shape’ler tercih.

Common mistakes

1. Animation her yere uygulamak.

.animation(.easeInOut)  // Deprecated, value olmadan

Bu tüm state change’leri animate ediyor. Unexpected behavior. value: parameter’ı zorunlu iOS 15+.

2. Nested animation chaos.

.animation(.easeIn)
.animation(.spring())  // Hangisi geçerli?

Order confusion. En son wins ama net değil. Tek animation modifier kullan.

3. matchedGeometryEffect namespace scope hatası.

İki view farklı parent’larda ama aynı namespace kullanıyor. Transition çalışmıyor.

Fix: Common parent’ta @Namespace tanımla, child’lara propagate et.

Sonuç

Implicit animation basit durumlar için (%70 use case). Explicit animation user-triggered transitions için. matchedGeometryEffect hero transitions için.

Doğru pattern doğru senaryoda kullanıldığında SwiftUI animation’ları Apple’ın native app’leriyle aynı kalitede. Pratik pattern bilgisi ile iyi UX mümkün.

Bu konuda bir projeniz mi var?

Kısa bir özet bırakın, 24 saat içinde size dönüş yapayım.

İletişime Geç