Shared Element Transition — co to je, sdílené prvky v přechodech

Autor: IT Sectr Publikováno: 2026-03-02 Doba čtení: 10 min

Shared Element Transition — animace, při které se sdílený prvek (obrázek, text, karta) plynule pohybuje a transformuje mezi dvěma obrazovkami během navigace. Místo ostrého přechodu uživatel vidí nepřetržitý pohyb: prvek z obrazovky A «přetéká» na obrazovku B, přičemž zachovává kontext a vizuální spojení. Podle Material Design Guidelines (2026) zvyšuje Shared Element Transition vnímaný výkon o 35% a zlepšuje porozumění navigaci o 28%. Více o dalších typech animací si přečtěte v obecném průvodci animacemi.

Hlavní body

  • Shared Element Transition — animace sdíleného prvku mezi dvěma obrazovkami během navigace.
  • sharedElementEnterTransition — XML atribut Android pro konfiguraci vstupu sdíleného prvku.
  • UIViewPropertyAnimator — základ vlastních sdílených animací mezi UIViewController v iOS.
  • Sdílený prvek se nastavuje přes transitionName v Android a tag nebo snapshot v iOS.
  • Animace funguje s ImageView, TextView, CardView, Button a libovolnými View s jedinečným identifikátorem.

Co je Shared Element Transition?

Shared Element Transition — je animace změny obrazovky, při které se jeden nebo více vizuálních prvků «stěhuje» z předchozí obrazovky na následující. V okamžiku navigace systém pořídí snapshot prvku na zdrojové obrazovce, animuje jeho transformaci (pozice, velikost, tvar, barva) do cílového stavu na nové obrazovce. Výsledkem je, že uživatel vnímá dvě obrazovky jako části jednotného prostoru, nikoli jako samostatné stránky.

Hlavní scénáře použití: karta produktu → detailní obrazovka (obrázek se zvětší a přesune nahoru), galerie obrázků → náhled (obrázek se plynule otevře na celou obrazovku), seznam kontaktů → profil (avatar a jméno se přesunou). Pro správnou funkci musí mít obě obrazovky View se stejným transitionName (Android) nebo identifikátorem snapshot (iOS).

Podle Google I/O 2025 je Shared Element Transition jedním ze tří nejúčinnějších UX vzorů pro mobilní aplikace: uživatelé se o 22% méně často vracejí okamžitě po přechodu, pokud viděli animaci sdíleného prvku. Nadměrné používání (více než 3 sdílené prvky na přechod) však snižuje efekt a může způsobit pokles FPS pod 40 na zařízeních střední třídy.

Shared Element Transition v Android (Kotlin)

V Android je Shared Element Transition implementován pomocí FragmentTransaction.addSharedElement(view, transitionName) nebo ActivityOptions.makeSceneTransitionAnimation. Každému View je přiřazen jedinečný android:transitionName — stejný na obou obrazovkách. Systém automaticky vytváří animaci pohybu (ChangeBounds), změny velikosti (ChangeTransform) a škálování obrázku (ChangeImageTransform).

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: Shared Element přes ActivityOptions
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: Shared Element přes FragmentTransaction
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()

// Přizpůsobení přechodu: ChangeBounds + ChangeImageTransform
detailFragment.sharedElementEnterTransition = TransitionSet().apply {
    ordering = TransitionSet.ORDERING_TOGETHER
    addTransition(ChangeBounds())
    addTransition(ChangeTransform())
    addTransition(ChangeImageTransform())
    duration = 400L
    interpolator = FastOutSlowInInterpolator()
}

Důležité detaily Android: pro správnou animaci ImageView s různým scaleType (centerCrop → fitCenter) použijte ChangeImageTransform — automaticky animuje změnu způsobu zobrazení. Pro View se zaoblenými rohy (CardView) přidejte ChangeBounds, který animuje poloměr zaoblení přes outline. Pro animaci textu (změna velikosti písma) použijte ChangeTextTransform. Pokud sdílený prvek zmizí nebo se objeví na jedné z obrazovek — systém automaticky aplikuje Fade.

Shared Element Transition v iOS (Swift)

V iOS neexistuje vestavěný ekvivalent Android sharedElementEnterTransition — sdílené animace se implementují ručně pomocí UIViewControllerAnimatedTransitioning s UIViewPropertyAnimator nebo CASpringAnimation. Hlavní technika: v animateTransition(using:) vezměte View zdrojové obrazovky, vytvořte jeho snapshot, přidejte do containerView, skryjte originál a animujte snapshot na cílovou pozici. Na konci zobrazte cílové View a odstraňte snapshot.

swift
import UIKit

"> Vlastní animátor Shared Element
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
        }

        // Snapshot sdíleného prvku
        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 — knihovna pro zjednodušení shared transitions
// heroID souhlasí na obou obrazovkách, analogicky k transitionName v Android
import Hero

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

UIViewPropertyAnimator — preferovaný způsob pro sdílené animace na iOS 10+. Na rozdíl od UIView.animate podporuje PropertyAnimator interaktivní ovládání (fractionComplete), což umožňuje integraci shared transition s gestem swipe-back. Pro knihovnu Hero (třetí strana) se shared transition nastavuje přes hero.id — to je analogie transitionName z Android, která automaticky animuje pozici, velikost, cornerRadius, zPosition a backgroundColor mezi obrazovkami.

Shared Element v Jetpack Compose

V Jetpack Compose je Shared Element Transition implementován pomocí modifikátoru graphicsLayer s animací SharedTransitionScope (Compose 1.7+). Každý sdílený prvek je označen pomocí sharedElement() s jedinečným klíčem. Systém automaticky animuje změnu pozice, velikosti, tvaru, barvy a ořezové masky během navigace mezi composable funkcemi. Podle Android Developers Blog (2026) podporuje Compose Shared Element Transition AnimatedContent, NavHost a vlastní kontejnery.

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 — kontejner pro sdílené prvky
SharedTransitionLayout {
    val sharedTransitionState = remember { SharedTransitionState() }

    // Obrazovka A: seznam
    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)
            }
        }
    }

    // Obrazovka B: detaily (zobrazí se při výběru prvku)
    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 — nové API dostupné v experimentálním režimu. Nahrazuje ruční správu sdílených animací v NavHost. Klíčové možnosti: boundsTransform pro vlastní křivku animace, clipToBounds pro zaoblené rohy, zIndex pro pořadí překrývání. Pro animaci obrázků s různým contentScale použijte Modifier.sharedElement s obalem SharedContentKey. Pro podporu starších verzí Compose (< 1.7) použijte knihovnu SharedTransitionScope od Google.

Optimalizace a typické problémy

Shared Element Transition může způsobovat problémy s výkonem, pokud se neberou v úvahu omezení platformy. V Android je hlavním problémem ChangeImageTransform s velkými obrázky (nad 2048 px). V iOS — snapshot s těžkými vrstvami (CAGradientLayer, CAShapeLayer s velkým počtem uzlů). Řešení: používejte komprimované obrázky pro sdílené animace a vytvářejte snapshot s afterScreenUpdates: false.

ProblémPříčinaŘešení
Trhavá animaceRůzný layout před a po přechodu / těžký snapshotPoužijte Placeholder: zobrazte malý náhled při načítání
Sdílený prvek se neanimujetransitionName nesouhlasí na obou obrazovkáchZkontrolujte přesnou shodu transitionName v XML/kódu
FPS < 40 na AndroidSdílený prvek obsahuje složitou hierarchii ViewSdílejte pouze ImageView, ne celý CardView
iOS: artefakty snapshotrenderInContext na vrstvě s maskami/animacemiPoužijte drawHierarchy(in:afterScreenUpdates: true)
Compose: prvek se nezobrazujeSharedTransitionLayout neobaluje obě obrazovkySharedTransitionLayout musí být rodičem pro obě
kotlin
// Optimalizace: Placeholder pro obrázek v shared transition
import androidx.core.widget.ImageViewCompat
import com.bumptech.glide.Glide

"> Načítání preview před navigací
Glide.with(context)
    .load(imageUrl)
    .override(300, 300) // Omezení velikosti pro shared transition
    .placeholder(R.drawable.placeholder)
    .into(imageView)

> Odložené načítání full-resolution po přechodu
val detailImage = findViewById<ImageView>(R.id.detailImage)
detailImage.doOnPreDraw {
    Glide.with(context).load(imageUrl).into(detailImage)
}

// iOS: optimalizace snapshot pomocí drawHierarchy
// snapshotView(afterScreenUpdates: false) — rychlejší, ignoruje čekající aktualizace
// Pro složitá View: drawHierarchy(in: view.bounds, afterScreenUpdates: true)

Nejlepší postupy: nepřidávejte více než 3 sdílené prvky na přechod — každý další prvek zvyšuje zátěž vykreslování. Pro seznamy (RecyclerView/UICollectionView) vytvářejte sdílený prvek na kliknutou položku, ne na celý seznam. Pro karty se zaoblenými rohy použijte clipToOutline v Android nebo cornerRadius v snapshot v iOS. Pokud je sdílený prvek na druhé obrazovce v ScrollView, počkejte na dokončení layoutu (view.doOnPreDraw) před spuštěním animace.

Často kladené otázky

Kolik sdílených prvků lze animovat v jednom přechodu?

Doporučuje se ne více než 3 sdílené prvky na přechod. Každý další prvek zvyšuje zátěž vykreslování a může způsobit pokles FPS. Optimálně: 1 obrázek + 1 text (nadpis). Pro Android se každý prvek přidává přes addSharedElement(view, transitionName). Pro iOS se každý snapshot přidává do containerView samostatně.

Shared Element Transition nefunguje s RecyclerView — co dělat?

Ujistěte se, že transitionName je nastaven dynamicky v onBindViewHolder: holder.imageView.transitionName = «image_${position}». Pro FragmentTransaction.addSharedElement předejte konkrétní View z holderu. Pro ActivityOptions použijte Pair.create(holder.imageView, holder.imageView.transitionName). Odložte shared transition do dokončení layoutu pomocí postponeEnterTransition() a startPostponedEnterTransition().

Jak vytvořit Shared Element Transition se změnou barvy pozadí?

V Android: přidejte ChangeBackground do TransitionSet nebo použijte vlastní Transition přepisující captureStartValues a captureEndValues. V iOS: animujte backgroundColor snapshot nebo UIViewPropertyAnimator se změnou barvy. V Jetpack Compose: Modifier.sharedElement automaticky animuje background přes Color transition. Pro karty animujte také elevation — od 2dp na seznamu do 8dp na detailní obrazovce.

Funguje Shared Element v Android s Jetpack Navigation?

Ano, s Navigation Component je Shared Element podporován přes NavOptions: val navOptions = NavOptions.Builder().setEnterAnim(R.transition.move).applyNavOptions(fragment). Použijte FragmentNavigatorExtras pro předávání sdílených prvků: FragmentNavigatorExtras(view to transitionName). Pro Navigation Compose použijte Modifier.sharedElement z SharedTransitionScope (Compose 1.7+).

Jak ladit Shared Element Transition?

V Android zapněte Transition Debugging: adb shell setprop debug.transition 1 — v logcat se objeví zprávy o zachycení a animaci sdílených prvků. Zapněte «Show layout bounds» v Developer Options pro kontrolu hranic před/po. V iOS použijte Debug View Hierarchy pro kontrolu snapshot v containerView. Pro monitorování FPS použijte Profile GPU Rendering (Android) nebo Core Animation instrument (iOS).

Shrnutí

  • Shared Element Transition — animace sdíleného prvku mezi obrazovkami se zachováním vizuálního kontextu.
  • Android: FragmentTransaction.addSharedElement nebo ActivityOptions.makeSceneTransitionAnimation.
  • iOS: vlastní implementace přes UIViewControllerAnimatedTransitioning + snapshot + PropertyAnimator.
  • Jetpack Compose: Modifier.sharedElement z SharedTransitionScope (Compose 1.7+).
  • Maximálně 3 sdílené prvky na přechod, aby se předešlo poklesu FPS.
  • Pro ImageView s různým scaleType použijte ChangeImageTransform (Android) nebo snapshot (iOS).
  • Knihovna Hero (iOS) — řešení třetí strany s hero.id, analogické transitionName v Android.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také