Shared Element Transition ایک اینیمیشن ہے جس میں ایک مشترکہ عنصر (تصویر، متن، کارڈ) نیویگیشن کے دوران دو اسکرینوں کے درمیان آسانی سے حرکت اور تبدیل ہوتا ہے۔ اچانک منتقلی کے بجائے، صارف مسلسل حرکت دیکھتا ہے: اسکرین A کا عنصر اسکرین B کی طرف بہتا ہے، سیاق و سباق اور بصری تعلق کو برقرار رکھتے ہوئے۔ Material Design Guidelines (2026) کے مطابق، Shared Element Transition سمجھی جانے والی کارکردگی کو 35% بڑھاتا ہے اور نیویگیشن کی سمجھ کو 28% بہتر بناتا ہے۔ اینیمیشن کی دیگر اقسام کے بارے میں مزید جاننے کے لیے عام اینیمیشن گائیڈ دیکھیں۔
اہم نکات
Shared Element Transition ایک اسکرین ٹرانزیشن اینیمیشن ہے جس میں ایک یا زیادہ بصری عناصر پچھلی اسکرین سے اگلی اسکرین میں منتقل ہوتے ہیں۔ نیویگیشن کے لمحے، نظام ماخذ اسکرین پر عنصر کا سنیپ شاٹ لیتا ہے، نئی اسکرین پر ہدف کی حالت میں اس کی تبدیلی (مقام، سائز، شکل، رنگ) کو اینیمیٹ کرتا ہے۔ نتیجے کے طور پر، صارف دو اسکرینوں کو علیحدہ صفحات کے بجائے ایک واحد جگہ کے حصوں کے طور پر سمجھتا ہے۔
بنیادی استعمال کے معاملات: پروڈکٹ کارڈ → تفصیلی اسکرین (تصویر بڑی ہو کر اوپر جاتی ہے)، تصویری گیلری → ویوور (تصویر آسانی سے پوری اسکرین میں پھیل جاتی ہے)، رابطہ فہرست → پروفائل (اوتار اور نام منتقل ہوتے ہیں)۔ صحیح کام کرنے کے لیے، دونوں اسکرینوں پر ایک ہی transitionName (Android) یا سنیپ شاٹ شناخت کنندہ (iOS) والے Views ہونے چاہئیں۔
Google I/O 2025 کے مطابق، Shared Element Transition موبائل ایپلیکیشنز کے لیے تین سب سے مؤثر UX پیٹرن میں سے ایک ہے: اگر صارفین نے مشترکہ عنصر کی اینیمیشن دیکھی ہو تو ٹرانزیشن کے فوراً بعد واپس آنے کا امکان 22% کم ہوتا ہے۔ تاہم، زیادہ استعمال (فی ٹرانزیشن 3 سے زیادہ مشترکہ عناصر) اثر کو کم کرتا ہے اور درمیانے درجے کے آلات پر FPS کو 40 سے نیچے گرا سکتا ہے۔
Android میں، Shared Element Transition FragmentTransaction.addSharedElement(view, transitionName) یا ActivityOptions.makeSceneTransitionAnimation کے ذریعے نافذ کیا جاتا ہے۔ ہر View کو ایک منفرد android:transitionName تفویض کیا جاتا ہے — دونوں اسکرینوں پر یکساں۔ نظام خود بخود حرکت کی اینیمیشن (ChangeBounds)، سائز تبدیل کرنے کی اینیمیشن (ChangeTransform) اور تصویر کو اسکیل کرنے کی اینیمیشن (ChangeImageTransform) بناتا ہے۔
<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" />
import androidx.core.app.ActivityOptionsCompat
import androidx.core.util.Pair
import androidx.fragment.app.FragmentTransaction
// Activity: ActivityOptions کے ذریعے 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 کے ذریعے 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()
// ٹرانزیشن حسب ضرورت: ChangeBounds + ChangeImageTransform
detailFragment.sharedElementEnterTransition = TransitionSet().apply {
ordering = TransitionSet.ORDERING_TOGETHER
addTransition(ChangeBounds())
addTransition(ChangeTransform())
addTransition(ChangeImageTransform())
duration = 400L
interpolator = FastOutSlowInInterpolator()
}
اہم Android تفصیلات: مختلف scaleType (centerCrop → fitCenter) والے ImageView کی درست اینیمیشن کے لیے، ChangeImageTransform استعمال کریں — یہ ڈسپلے موڈ کی تبدیلی کو خود بخود اینیمیٹ کرتا ہے۔ گول کونوں والے Views (CardView) کے لیے، ChangeBounds شامل کریں، جو آؤٹ لائن کے ذریعے کونے کے رداس کو اینیمیٹ کرتا ہے۔ متن کی اینیمیشن (فونٹ سائز تبدیل) کے لیے، ChangeTextTransform استعمال کریں۔ اگر کوئی مشترکہ عنصر کسی ایک اسکرین پر غائب یا ظاہر ہوتا ہے، تو نظام خود بخود Fade لاگو کرتا ہے۔
iOS میں Android کے sharedElementEnterTransition کا کوئی بلٹ ان مساوی نہیں ہے — مشترکہ اینیمیشنز UIViewControllerAnimatedTransitioning کے ذریعے UIViewPropertyAnimator یا CASpringAnimation استعمال کرتے ہوئے دستی طور پر نافذ کی جاتی ہیں۔ بنیادی تکنیک: animateTransition(using:) میں، ماخذ اسکرین کا View حاصل کریں، اس کا سنیپ شاٹ لیں، اسے containerView میں شامل کریں، اصل کو چھپائیں، اور سنیپ شاٹ کو ہدف کی پوزیشن پر اینیمیٹ کریں۔ آخر میں، ہدف View دکھائیں اور سنیپ شاٹ ہٹا دیں۔
import UIKit
"> 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
}
// مشترکہ عنصر کا سنیپ شاٹ
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 — مشترکہ ٹرانزیشن آسان بنانے کے لیے لائبریری
// heroID دونوں اسکرینوں پر مماثل ہے، Android میں transitionName سے مشابہ
import Hero
imageView.hero.id = "itemImage"
imageView.hero.modifiers = [.spring(stiffness: 300, damping: 20)]
titleLabel.hero.id = "itemTitle"
UIViewPropertyAnimator iOS 10+ پر مشترکہ اینیمیشن کے لیے ترجیحی طریقہ ہے۔ UIView.animate کے برعکس، PropertyAnimator انٹرایکٹو کنٹرول (fractionComplete) کو سپورٹ کرتا ہے، جو مشترکہ ٹرانزیشن کو سوائپ بیک جیسچر کے ساتھ ضم کرنے کی اجازت دیتا ہے۔ Hero لائبریری (تیسرے فریق) کے لیے، مشترکہ ٹرانزیشن hero.id کے ذریعے سیٹ کی جاتی ہے — یہ Android کے transitionName کا مشابہ ہے، جو اسکرینوں کے درمیان مقام، سائز، cornerRadius، zPosition اور backgroundColor کو خود بخود اینیمیٹ کرتا ہے۔
Jetpack Compose میں، Shared Element Transition graphicsLayer موڈیفائر کے ذریعے SharedTransitionScope اینیمیشن (Compose 1.7+) کے ساتھ نافذ کیا جاتا ہے۔ ہر مشترکہ عنصر کو ایک منفرد کلید کے ساتھ sharedElement() کے ذریعے نشان زد کیا جاتا ہے۔ نظام composable فنکشنز کے درمیان نیویگیٹ کرتے وقت مقام، سائز، شکل، رنگ اور کلپ ماسک کی تبدیلیوں کو خود بخود اینیمیٹ کرتا ہے۔ Android Developers Blog (2026) کے مطابق، Compose Shared Element Transition AnimatedContent، NavHost اور حسب ضرورت کنٹینرز کو سپورٹ کرتا ہے۔
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 — مشترکہ عناصر کے لیے کنٹینر
SharedTransitionLayout {
val sharedTransitionState = remember { SharedTransitionState() }
// اسکرین A: فہرست
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)
}
}
}
// اسکرین B: تفصیلات (عنصر منتخب کرنے پر دکھائی جاتی ہیں)
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 — ایک نیا API جو تجرباتی موڈ میں دستیاب ہے۔ یہ NavHost میں دستی مشترکہ اینیمیشن مینجمنٹ کو تبدیل کرتا ہے۔ اہم خصوصیات: حسب ضرورت اینیمیشن وکر کے لیے boundsTransform، گول کونوں کے لیے clipToBounds، اسٹیکنگ آرڈر کے لیے zIndex۔ مختلف contentScale والی تصاویر کو اینیمیٹ کرنے کے لیے، SharedContentKey ریپر کے ساتھ Modifier.sharedElement استعمال کریں۔ پرانے Compose ورژنز (< 1.7) کے لیے، Google کی SharedTransitionScope لائبریری استعمال کریں۔
Shared Element Transition کارکردگی کے مسائل پیدا کر سکتا ہے اگر پلیٹ فارم کی حدود پر غور نہ کیا جائے۔ Android میں، بنیادی مسئلہ بڑی تصاویر (2048 px سے اوپر) کے ساتھ ChangeImageTransform ہے۔ iOS میں — بھاری تہوں (CAGradientLayer، کئی نوڈس والا CAShapeLayer) کے ساتھ سنیپ شاٹ۔ حل: مشترکہ اینیمیشن کے لیے کمپریسڈ تصاویر استعمال کریں اور afterScreenUpdates: false کے ساتھ سنیپ شاٹ لیں۔
| مسئلہ | وجہ | حل |
|---|---|---|
| اینیمیشن میں رکاوٹ | ٹرانزیشن سے پہلے/بعد میں مختلف لے آؤٹ / بھاری سنیپ شاٹ | Placeholder استعمال کریں: لوڈ ہوتے وقت چھوٹا پیش منظر دکھائیں |
| مشترکہ عنصر اینیمیٹ نہیں ہو رہا | دونوں اسکرینوں پر transitionName مماثل نہیں ہے | XML/کوڈ میں transitionName کے صحیح مماثلت کی جانچ کریں |
| Android پر FPS < 40 | مشترکہ عنصر میں پیچیدہ View درجہ بندی ہے | پوری CardView نہیں، صرف ImageView شیئر کریں |
| iOS: سنیپ شاٹ آرٹفیکٹس | ماسک/اینیمیشن والی تہہ پر renderInContext | drawHierarchy(in:afterScreenUpdates: true) استعمال کریں |
| Compose: عنصر ظاہر نہیں ہو رہا | SharedTransitionLayout دونوں اسکرینوں کو گھیرے نہیں | SharedTransitionLayout دونوں کا والدین ہونا چاہیے |
// بهتری: مشترکہ ٹرانزیشن میں تصویر کے لیے Placeholder
import androidx.core.widget.ImageViewCompat
import com.bumptech.glide.Glide
"> نیویگیشن سے پہلے preview لوڈ کرنا
Glide.with(context)
.load(imageUrl)
.override(300, 300) // مشترکہ ٹرانزیشن کے لیے سائز کی حد
.placeholder(R.drawable.placeholder)
.into(imageView)
> ٹرانزیشن کے بعد full-resolution کی تاخیر سے لوڈنگ
val detailImage = findViewById<ImageView>(R.id.detailImage)
detailImage.doOnPreDraw {
Glide.with(context).load(imageUrl).into(detailImage)
}
// iOS: drawHierarchy کے ساتھ سنیپ شاٹ کی بهتری
// snapshotView(afterScreenUpdates: false) — تیز، زیر التوا اپڈیٹس کو نظر انداز کرتا ہے
// پیچیدہ Views کے لیے: drawHierarchy(in: view.bounds, afterScreenUpdates: true)
بہترین طریقے: فی ٹرانزیشن 3 سے زیادہ مشترکہ عناصر شامل نہ کریں — ہر اضافی عنصر رینڈرنگ بوجھ بڑھاتا ہے۔ فہرستوں (RecyclerView/UICollectionView) کے لیے، مشترکہ عنصر کو پوری فہرست کے بجائے کلک کردہ آئٹم پر بنائیں۔ گول کونوں والے کارڈز کے لیے، Android پر clipToOutline یا iOS پر سنیپ شاٹ میں cornerRadius استعمال کریں۔ اگر دوسری اسکرین پر مشترکہ عنصر ScrollView کے اندر ہے، تو اینیمیشن شروع کرنے سے پہلے لے آؤٹ مکمل ہونے (view.doOnPreDraw) کا انتظار کریں۔
اکثر پوچھے گئے سوالات
فی ٹرانزیشن 3 مشترکہ عناصر سے زیادہ استعمال نہ کرنے کی سفارش کی جاتی ہے۔ ہر اضافی عنصر رینڈرنگ بوجھ بڑھاتا ہے اور FPS گرنے کا سبب بن سکتا ہے۔ بہترین: 1 تصویر + 1 متن (عنوان)۔ Android کے لیے، ہر عنصر addSharedElement(view, transitionName) کے ذریعے شامل کیا جاتا ہے۔ iOS کے لیے، ہر سنیپ شاٹ containerView میں الگ سے شامل کیا جاتا ہے۔
یقینی بنائیں کہ transitionName onBindViewHolder میں متحرک طور پر سیٹ کیا گیا ہے: holder.imageView.transitionName = "image_${position}"۔ FragmentTransaction.addSharedElement کے لیے، holder سے مخصوص View پاس کریں۔ ActivityOptions کے لیے، Pair.create(holder.imageView, holder.imageView.transitionName) استعمال کریں۔ postponeEnterTransition() اور startPostponedEnterTransition() کے ذریعے لے آؤٹ مکمل ہونے تک مشترکہ ٹرانزیشن میں تاخیر کریں۔
Android میں: TransitionSet میں ChangeBackground شامل کریں یا حسب ضرورت Transition استعمال کریں جو captureStartValues اور captureEndValues کو اوور رائڈ کرے۔ iOS میں: سنیپ شاٹ کے backgroundColor کو اینیمیٹ کریں یا رنگ کی تبدیلی کے ساتھ UIViewPropertyAnimator استعمال کریں۔ Jetpack Compose میں: Modifier.sharedElement Color ٹرانزیشن کے ذریعے خود بخود پس منظر کو اینیمیٹ کرتا ہے۔ کارڈز کے لیے، elevation کو بھی اینیمیٹ کریں — فہرست میں 2dp سے تفصیلی اسکرین پر 8dp تک۔
ہاں، Navigation Component کے ساتھ، Shared Element NavOptions کے ذریعے سپورٹ کیا جاتا ہے: val navOptions = NavOptions.Builder().setEnterAnim(R.transition.move).applyNavOptions(fragment)۔ مشترکہ عناصر پاس کرنے کے لیے FragmentNavigatorExtras استعمال کریں: FragmentNavigatorExtras(view to transitionName)۔ Navigation Compose کے لیے، SharedTransitionScope (Compose 1.7+) سے Modifier.sharedElement استعمال کریں۔
Android میں، Transition Debugging فعال کریں: adb shell setprop debug.transition 1 — logcat میں مشترکہ عنصر کی گرفتاری اور اینیمیشن کے بارے میں پیغامات ظاہر ہوں گے۔ Developer Options میں "Show layout bounds" فعال کریں تاکہ پہلے/بعد کی حدود چیک کریں۔ iOS میں، containerView میں سنیپ شاٹ چیک کرنے کے لیے Debug View Hierarchy استعمال کریں۔ FPS کے لیے، Profile GPU Rendering (Android) یا Core Animation آلہ (iOS) استعمال کریں۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں