Shared Element Transition — nedir, geçişlerde ortak öğeler

Yazar: IT Sectr Yayınlanma: 2026-03-02 Okuma süresi: 10 dk

Shared Element Transition, gezinme sırasında ortak bir öğenin (resim, metin, kart) iki ekran arasında yumuşak bir şekilde hareket ettiği ve dönüştüğü bir animasyondur. Ani bir geçiş yerine kullanıcı sürekli bir hareket görür: A ekranındaki öğe B ekranına akar, bağlamı ve görsel bağlantıyı korur. Material Design Guidelines'a (2026) göre Shared Element Transition, algılanan performansı %35 artırır ve gezinme anlayışını %28 iyileştirir. Diğer animasyon türleri hakkında daha fazla bilgi için genel animasyon kılavuzuna bakın.

Önemli Noktalar

  • Shared Element Transition — gezinme sırasında iki ekran arasında ortak bir öğenin animasyonu.
  • sharedElementEnterTransition — ortak öğe girişini yapılandırmak için Android XML özelliği.
  • UIViewPropertyAnimator — iOS'ta UIViewController'lar arasında özel ortak animasyonların temeli.
  • Ortak öğe, Android'de transitionName ve iOS'ta tag veya snapshot ile tanımlanır.
  • Animasyon, ImageView, TextView, CardView, Button ve benzersiz tanımlayıcıya sahip herhangi bir View ile çalışır.

Shared Element Transition Nedir?

Shared Element Transition, bir veya daha fazla görsel öğenin önceki ekrandan sonraki ekrana taşındığı bir ekran geçiş animasyonudur. Gezinme anında sistem, kaynak ekrandaki öğenin bir anlık görüntüsünü alır, yeni ekrandaki hedef duruma dönüşümünü (konum, boyut, şekil, renk) canlandırır. Sonuç olarak kullanıcı, iki ekranı ayrı sayfalar yerine tek bir alanın parçaları olarak algılar.

Ana kullanım durumları: ürün kartı → detay ekranı (resim büyür ve yukarı hareket eder), resim galerisi → görüntüleyici (resim tüm ekrana yumuşakça genişler), kişi listesi → profil (avatar ve ad taşınır). Doğru çalışma için her iki ekranda da aynı transitionName (Android) veya anlık görüntü tanımlayıcısına (iOS) sahip View'ler bulunmalıdır.

Google I/O 2025'e göre Shared Element Transition, mobil uygulamalar için en etkili üç UX modelinden biridir: Ortak öğe animasyonunu gören kullanıcıların geçişten hemen sonra geri dönme olasılığı %22 daha düşüktür. Ancak aşırı kullanım (geçiş başına 3'ten fazla ortak öğe) etkiyi azaltır ve orta sınıf cihazlarda FPS'nin 40'ın altına düşmesine neden olabilir.

Android'de Shared Element Transition (Kotlin)

Android'de Shared Element Transition, FragmentTransaction.addSharedElement(view, transitionName) veya ActivityOptions.makeSceneTransitionAnimation aracılığıyla uygulanır. Her View'e benzersiz bir android:transitionName atanır — her iki ekranda aynı. Sistem otomatik olarak bir hareket animasyonu (ChangeBounds), bir boyut değiştirme animasyonu (ChangeTransform) ve bir resim ölçekleme animasyonu (ChangeImageTransform) oluşturur.

xml

<androidx.cardview.widget.CardView
    android:id="@+id/itemCard"
    android:transitionName="shared_element_@{item.id}"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/itemImage"
        android:transitionName="shared_image_@{item.id}" />
</androidx.cardview.widget.CardView>


<ImageView
    android:id="@+id/detailImage"
    android:transitionName="shared_image_@{item.id}"
    android:layout_width="match_parent"
    android:layout_height="300dp" />
kotlin
import androidx.core.app.ActivityOptionsCompat
import androidx.core.util.Pair
import androidx.fragment.app.FragmentTransaction

// Activity: ActivityOptions ile Shared Element
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(
    this,
    Pair(imageView, getString(R.string.transition_name_image)),
    Pair(titleView, getString(R.string.transition_name_title))
)
startActivity(intent, options.toBundle())

// Fragment: FragmentTransaction ile Shared Element
val detailFragment = DetailFragment().apply {
    sharedElementEnterTransition = TransitionInflater.from(context)
        .inflateTransition(android.R.transition.move)
    sharedElementReturnTransition = TransitionInflater.from(context)
        .inflateTransition(android.R.transition.move)
}

supportFragmentManager.beginTransaction()
    .addSharedElement(imageView, imageView.transitionName)
    .addSharedElement(titleView, titleView.transitionName)
    .replace(R.id.container, detailFragment)
    .addToBackStack(null)
    .commit()

// Geçiş Özelleştirmesi: ChangeBounds + ChangeImageTransform
detailFragment.sharedElementEnterTransition = TransitionSet().apply {
    ordering = TransitionSet.ORDERING_TOGETHER
    addTransition(ChangeBounds())
    addTransition(ChangeTransform())
    addTransition(ChangeImageTransform())
    duration = 400L
    interpolator = FastOutSlowInInterpolator()
}

Önemli Android Detayları: farklı scaleType (centerCrop → fitCenter) ile ImageView'ün doğru animasyonu için ChangeImageTransform kullanın — görüntüleme modu değişikliğini otomatik olarak canlandırır. Yuvarlak köşeli Views (CardView) için, anahattı kullanarak köşe yarıçapını canlandıran ChangeBounds ekleyin. Metin animasyonu (yazı tipi boyutu değişikliği) için ChangeTextTransform kullanın. Ortak bir öğe ekranlardan birinde kaybolur veya görünürse, sistem otomatik olarak Fade uygular.

iOS'ta Shared Element Transition (Swift)

iOS, Android'in sharedElementEnterTransition'ının yerleşik bir eşdeğerine sahip değildir — ortak animasyonlar UIViewControllerAnimatedTransitioning aracılığıyla UIViewPropertyAnimator veya CASpringAnimation kullanılarak manuel olarak uygulanır. Ana teknik: animateTransition(using:) içinde kaynak ekranın View'ini alın, bir anlık görüntü alın, containerView'a ekleyin, orijinali gizleyin ve anlık görüntüyü hedef konuma canlandırın. Sonunda hedef View'ı gösterin ve anlık görüntüyü kaldırın.

swift
import UIKit

"> Özel Shared Element animatörü
class SharedElementAnimator: NSObject, UIViewControllerAnimatedTransitioning {
    let fromView: UIView
    let toView: UIView
    let fromFrame: CGRect
    let toFrame: CGRect
    let isPresenting: Bool

    func transitionDuration(using ctx: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.4
    }

    func animateTransition(using ctx: UIViewControllerContextTransitioning) {
        guard let toVC = ctx.viewController(forKey: .to),
              let fromVC = ctx.viewController(forKey: .from)
        else { return }

        let container = ctx.containerView

        if isPresenting {
            container.addSubview(toVC.view)
            toVC.view.layoutIfNeeded()
            toVC.view.alpha = 0
        }

        // Ortak öğe anlık görüntüsü
        let snapshot = fromView.snapshotView(afterScreenUpdates: false)!
        snapshot.frame = isPresenting ? fromFrame : toFrame
        snapshot.layer.cornerRadius = isPresenting ? 12 : 0
        container.addSubview(snapshot)

        fromView.isHidden = true
        toView.isHidden = true

        UIView.animate(withDuration: transitionDuration(using: ctx),
                       delay: 0,
                       usingSpringWithDamping: 0.7,
                       initialSpringVelocity: 0) {
            snapshot.frame = self.isPresenting ? self.toFrame : self.fromFrame
            snapshot.layer.cornerRadius = self.isPresenting ? 0 : 12
            toVC.view.alpha = 1
        } completion: { _ in
            fromView.isHidden = false
            toView.isHidden = false
            snapshot.removeFromSuperview()
            ctx.completeTransition(!ctx.transitionWasCancelled)
        }
    }
}

// Hero — ortak geçişleri basitleştirmek için kitaplık
// heroID her iki ekranda eşleşir, Android'deki transitionName'e benzer
import Hero

imageView.hero.id = "itemImage"
imageView.hero.modifiers = [.spring(stiffness: 300, damping: 20)]
titleLabel.hero.id = "itemTitle"

UIViewPropertyAnimator, iOS 10+'ta ortak animasyonlar için tercih edilen yöntemdir. UIView.animate'in aksine, PropertyAnimator etkileşimli kontrolü (fractionComplete) destekleyerek ortak geçişin geri kaydırma hareketiyle entegre edilmesini sağlar. Hero kitaplığı (üçüncü taraf) için ortak geçiş, hero.id aracılığıyla ayarlanır — bu, Android'deki transitionName'in bir benzeridir ve ekranlar arasında konumu, boyutu, cornerRadius'u, zPosition'ı ve backgroundColor'yi otomatik olarak canlandırır.

Jetpack Compose'ta Shared Element

Jetpack Compose'ta Shared Element Transition, graphicsLayer değiştiricisi aracılığıyla SharedTransitionScope animasyonu (Compose 1.7+) ile uygulanır. Her ortak öğe, benzersiz bir anahtarla sharedElement() kullanılarak işaretlenir. Sistem, composable işlevler arasında gezinirken konum, boyut, şekil, renk ve kırpma maskesi değişikliklerini otomatik olarak canlandırır. Android Developers Blog'a (2026) göre Compose Shared Element Transition, AnimatedContent, NavHost ve özel kapsayıcıları destekler.

kotlin
import androidx.compose.animation.core.spring
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.SharedTransitionLayout
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut

// SharedTransitionLayout — ortak öğeler için kapsayıcı
SharedTransitionLayout {
    val sharedTransitionState = remember { SharedTransitionState() }

    // Ekran A: liste
    LazyColumn {
        items(items) { item ->
            Card(
                modifier = Modifier.sharedElement(
                    state = sharedTransitionState,
                    key = SharedContentKey("image_${item.id}"),
                    boundsTransform = { _, _ ->
                        spring(dampingRatio = Spring.DampingRatioMediumBouncy)
                    }
                )
            ) {
                AsyncImage(model = item.imageUrl, contentDescription = null)
            }
        }
    }

    // Ekran B: detaylar (bir öğe seçildiğinde gösterilir)
    AnimatedContent(
        targetState = selectedItem,
        transitionSpec = {
            EnterTransition.None using SizeTransform(clip = false)
        }
    ) { item ->
        item?.let {
            Image(
                modifier = Modifier.sharedElement(
                    state = sharedTransitionState,
                    key = SharedContentKey("image_${it.id}")
                ),
                painter = rememberAsyncImagePainter(it.imageUrl),
                contentDescription = null,
                contentScale = ContentScale.FillWidth
            )
        }
    }
}

Compose 1.7+ SharedTransitionScope — deneysel modda kullanılabilen yeni bir API. NavHost'ta manuel ortak animasyon yönetiminin yerini alır. Temel özellikler: özel animasyon eğrisi için boundsTransform, yuvarlak köşeler için clipToBounds, yığınlama sırası için zIndex. Farklı contentScale ile görüntüleri canlandırmak için SharedContentKey sarmalayıcısıyla Modifier.sharedElement kullanın. Eski Compose sürümleri (< 1.7) için Google'ın SharedTransitionScope kitaplığını kullanın.

Optimizasyon ve Yaygın Sorunlar

Shared Element Transition, platform sınırlamaları dikkate alınmazsa performans sorunlarına neden olabilir. Android'de ana sorun, büyük resimlerle (2048 px üzeri) ChangeImageTransform'dur. iOS'ta — ağır katmanlarla (CAGradientLayer, çok sayıda düğüme sahip CAShapeLayer) anlık görüntü. Çözüm: ortak animasyon için sıkıştırılmış resimler kullanın ve afterScreenUpdates: false ile anlık görüntü alın.

SorunNedenÇözüm
Takılma animasyonuGeçiş öncesi/sonrası farklı düzen / ağır anlık görüntüPlaceholder kullanın: yüklenirken küçük önizleme gösterin
Ortak öğe canlandırılmıyortransitionName her iki ekranda eşleşmiyorXML/kodda transitionName'in tam eşleşmesini kontrol edin
Android'de FPS < 40Ortak öğe karmaşık View hiyerarşisi içeriyorTüm CardView'ı değil, yalnızca ImageView'ı paylaşın
iOS: anlık görüntü yapaylıklarıMaskeler/animasyonlar içeren katmanda renderInContextdrawHierarchy(in:afterScreenUpdates: true) kullanın
Compose: öğe görünmüyorSharedTransitionLayout her iki ekranı sarmıyorSharedTransitionLayout her ikisinin de üst öğesi olmalıdır
kotlin
// Optimizasyon: Ortak geçişte resim için Placeholder
import androidx.core.widget.ImageViewCompat
import com.bumptech.glide.Glide

"> Navigasyondan önce preview yükleme
Glide.with(context)
    .load(imageUrl)
    .override(300, 300) // Ortak geçiş için boyut sınırı
    .placeholder(R.drawable.placeholder)
    .into(imageView)

> Geçişten sonra full-resolution gecikmeli yükleme
val detailImage = findViewById<ImageView>(R.id.detailImage)
detailImage.doOnPreDraw {
    Glide.with(context).load(imageUrl).into(detailImage)
}

// iOS: drawHierarchy ile anlık görüntü optimizasyonu
// snapshotView(afterScreenUpdates: false) — daha hızlı, bekleyen güncellemeleri yok sayar
// Karmaşık View'ler için: drawHierarchy(in: view.bounds, afterScreenUpdates: true)

En iyi uygulamalar: geçiş başına 3'ten fazla ortak öğe eklemeyin — her ek öğe işleme yükünü artırır. Listeler (RecyclerView/UICollectionView) için ortak öğeyi tüm liste yerine tıklanan öğede yapın. Yuvarlak köşeli kartlar için Android'de clipToOutline veya iOS'ta anlık görüntüde cornerRadius kullanın. İkinci ekrandaki ortak öğe bir ScrollView içindeyse, animasyonu başlatmadan önce düzenin tamamlanmasını bekleyin (view.doOnPreDraw).

Sıkça Sorulan Sorular

Bir geçişte kaç ortak öğe canlandırılabilir?

Geçiş başına 3 ortak öğeden fazla kullanılmaması önerilir. Her ek öğe işleme yükünü artırır ve FPS düşüşlerine neden olabilir. Optimal: 1 resim + 1 metin (başlık). Android için her öğe addSharedElement(view, transitionName) ile eklenir. iOS için her anlık görüntü containerView'a ayrı ayrı eklenir.

Shared Element Transition RecyclerView ile çalışmıyor — ne yapmalı?

transitionName'in onBindViewHolder'da dinamik olarak ayarlandığından emin olun: holder.imageView.transitionName = "image_${position}". FragmentTransaction.addSharedElement için holder'dan belirli View'ı iletin. ActivityOptions için Pair.create(holder.imageView, holder.imageView.transitionName) kullanın. postponeEnterTransition() ve startPostponedEnterTransition() ile düzen tamamlanana kadar ortak geçişi geciktirin.

Arka plan rengi değişikliği ile Shared Element Transition nasıl yapılır?

Android'de: TransitionSet'e ChangeBackground ekleyin veya captureStartValues ve captureEndValues'ı geçersiz kılan özel bir Transition kullanın. iOS'ta: anlık görüntünün backgroundColor'ını canlandırın veya renk değişikliğiyle UIViewPropertyAnimator kullanın. Jetpack Compose'ta: Modifier.sharedElement, Color geçişi aracılığıyla arka planı otomatik olarak canlandırır. Kartlar için elevation'ı da canlandırın — listede 2dp'den detay ekranında 8dp'ye.

Android'de Jetpack Navigation ile Shared Element çalışır mı?

Evet, Navigation Component ile Shared Element, NavOptions aracılığıyla desteklenir: val navOptions = NavOptions.Builder().setEnterAnim(R.transition.move).applyNavOptions(fragment). Ortak öğeleri iletmek için FragmentNavigatorExtras kullanın: FragmentNavigatorExtras(view to transitionName). Navigation Compose için SharedTransitionScope'den (Compose 1.7+) Modifier.sharedElement kullanın.

Shared Element Transition nasıl hata ayıklanır?

Android'de Transition Debugging'i etkinleştirin: adb shell setprop debug.transition 1 — logcat'te ortak öğe yakalama ve animasyonu hakkında mesajlar görünecektir. Sınırları kontrol etmek için Developer Options'da "Show layout bounds" seçeneğini etkinleştirin. iOS'ta containerView'daki anlık görüntüyü kontrol etmek için Debug View Hierarchy'yi kullanın. FPS için Profile GPU Rendering (Android) veya Core Animation aracını (iOS) kullanın.

Özet

  • Shared Element Transition — görsel bağlamı koruyarak ekranlar arasında ortak öğe animasyonu.
  • Android: FragmentTransaction.addSharedElement veya ActivityOptions.makeSceneTransitionAnimation.
  • iOS: UIViewControllerAnimatedTransitioning + anlık görüntü + PropertyAnimator aracılığıyla özel uygulama.
  • Jetpack Compose: SharedTransitionScope'den (Compose 1.7+) Modifier.sharedElement.
  • FPS düşüşlerini önlemek için geçiş başına maksimum 3 ortak öğe.
  • Farklı scaleType'taki ImageView için ChangeImageTransform (Android) veya anlık görüntü (iOS) kullanın.
  • Hero kitaplığı (iOS) — hero.id ile üçüncü taraf çözüm, Android'deki transitionName'e benzer.

Anahtar teslim bir mobil uygulama geliştireceğiz

IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.

Projeyi tartış

Ayrıca okuyun