Drag-and-Drop — یک حرکت انتراکتیوی کشیدن شیئه مرئی با انگشت بر روی صفحه نمایش است. این حرکت از سه مرحله تشکیل شده: گیرفتاری (long press یا لمس)، جابجایی (حرکت انگشت با شیء) و رها کردن (انداختن در منطقه مقصد). برای تغییر ترتیب عناصر، انتقال فایلها بین پوشهها، سازماندهی رابط (drag to reorder, drag to resize) استفاده میشود. در iOS از طریق UIDragInteraction و در Android از طریق DragEvent و startDragAndDrop پیاده سازی میشود.
نکات کلیدی
Drag-and-Drop (DnD، کشیدن و رها کردن) — روشی برای مانیپولاسیون عناصر گرافیکی رابط است که در آن کاربر شیءی را با انگشت یا ماوس گرفته، آن را به مکانی دیگر منتقل کرده و رها میکند. در رابطهای موبایل، Drag-and-Drop معمولاً با فشار دراز (Long Press) آغاز میشود — کاربر عنصری را به مدت 300–500 میلیثانیه نگه میدارد، سپس سیستم رابط را به حالت کشیدن تغییر میدهد و حرکت انگشت شیء را دنبال میکند.
از نظر تاریخی، Drag-and-Drop اولین بار در رابطهای رومیزی (Xerox Star، 1981؛ Macintosh، 1984) به عنوان جایگزینی برای دستورات کپی/چسباندن ظاهر شد. در پلتفرمهای موبایل، پشتیبانی کامل در iOS 11 (2017) با UIDragInteraction و در Android 7.0 (2016) با Multi-Window Drag-and-Drop ارائه شد. قبل از آن، کشیدن و رها کردن در برنامههای موبایل از طریق راهحلهای سفارشی با gesture recognizers پیاده سازی میشد.
مفاهیم کلیدی: drag session (جلسه کشیدن)، drag items (اشیاء کشیده شده)، drag preview (تصویر مرئی شیء)، drop target (منطقه رهایش)، drop proposal (عمل در زمان رهایش: انتقال، کپی، اتصال). سیستم جلسه را در سطح سیستمعامل مدیریت میکند که امکان کشیدن اشیاء را بین برنامههای مختلف فراهم میکند (مثلاً، عکس از گالری به یادداشتها).
Drag-and-Drop به سه مرحله پیاپی تقسیم میشود. مرحله 1 — گیرفتاری (Drag Start): کاربر حرکت را آغاز میکند (معمولاً Long Press)، سیستم یک جلسه کشیدن ایجاد میکند، دادههای شیء را ضبط میکند و drag preview — یک کپی نیمه شفاف از عنصر که انگشت را دنبال میکند را نمایش میدهد. در این مرحله، سیستم تا پایان DnD سایر حرکات (پیمایش، انتخاب متن) را رد میکند.
مرحله 2 — جابجایی (Drag Move): انگشت بر روی صفحه حرکت میکند، drag preview نقطه لمس را دنبال میکند. سیستم به طور مداوم drop target زیر انگشت را تعیین کرده و رویدادهای drag enter، drag move و drag exit را ارسال میکند. Drop target میتواند به صورت بصری واکنش نشان دهد: روشن شود، گسترش یابد یا خط درج نشان دهد. این مرحله بسته به پیچیدگی کار از 0.5 تا 10+ ثانیه طول میکشد.
مرحله 3 — رها کردن (Drop): انگشت از صفحه جدا میشود. سیستم جلسه کشیدن را پایان میدهد و در عنصر هدف، drop callback را فراخوان میکند. اگر drop target رهایش را رد کند (مثلاً، کشیدن فایل به پوشهای بدون مجوز نوشتن)، drag preview با انیمیشن به مکان اصلی خود باز میگردد (cancel animation). اگر رهایش پذیرفته شود، عنصر از مکان اصلی ناپدید شده و در مکان مقصد ظاهر میشود.
| مرحله | رویداد iOS | رویداد Android | وضعیت بصری |
|---|---|---|---|
| گیرفتاری | UIDragInteractionDelegate.dragSessionWillBegin | View.OnLongClickListener → startDragAndDrop | Drag preview زیر انگشت ظاهر میشود |
| جابجایی | UIDropInteractionDelegate.dragSessionDidUpdate | View.OnDragListener.onDrag (ACTION_DRAG_MOVED) | Preview حرکت میکند، target روشن میشود |
| رها کردن | UIDropInteractionDelegate.performDrop | View.OnDragListener.onDrag (ACTION_DROP) | انیمیشن رهایش یا بازگشت |
کشیدن بین برنامهای — قابلیتی که در هر دو پلتفرم موجود است. iOS از Drag-and-Drop بین برنامهها در حالت Split View و Slide Over (iPad) پشتیبانی میکند. Android از کشیدن بین برنامهها در حالت Split Screen پشتیبانی میکند. دادهها از طریق UIPasteConfiguration (iOS) یا ClipData (Android) با مشخص کردن نوعهای پشتیبانی شده (UTI برای iOS، MIME برای Android) انتقال مییابند.
UIDragInteraction — API اصلی iOS برای کشیدن، اضافه شده در iOS 11. برای فعال کردن کشیدن از View، UIDragInteraction را با UIDragInteractionDelegate اضافه کنید. دلگات مشخص میکند که کدام عناصر کشیده میشوند، ظاهر و رفتار آنها. برای پذیرش عناصر از UIDropInteraction با UIDropInteractionDelegate استفاده میشود که مشخص میکند آیا View میتواند رهایش را بپذیرد و چگونه دادهها را پردازش کند.
// UIDragInteraction — کشیدن عنصر از کلکشن
class DragViewController: UIViewController, UIDragInteractionDelegate {
@IBOutlet var dragSourceView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let dragInteraction = UIDragInteraction(delegate: self)
dragSourceView.addInteraction(dragInteraction)
}
func dragInteraction(_ interaction: UIDragInteraction,
itemsForBeginning session: UIDragSession) -> [UIDragItem] {
guard let data = Data(contentsOf: selectedFileURL) else { return [] }
let itemProvider = NSItemProvider(object: data as NSData)
let dragItem = UIDragItem(itemProvider: itemProvider)
// Drag preview — نسخه کوچکشده عنصر
dragItem.previewProvider = {
let previewView = UIImageView(image: thumbnailImage)
previewView.alpha = 0.8
return UIDragPreview(view: previewView)
}
return [dragItem]
}
}UIDropInteractionDelegate — رابط آیینهای برای پذیرش رهایش. روش canHandle(_:) بررسی میکند که آیا نوع داده پشتیبانی میشود. sessionDidUpdate(_:) وضعیت را به صورت بصری بهروز میکند (روشنایی، خط درج). performDrop(_:) پردازش نهایی دادهها را انجام میدهد. iOS به طور آتوماتیک UIDropProposal را با نوع عملیات فراخوان میکند: .move (انتقال)، .copy (کپی)، .forbidden (ممنوع). نوع عملیات بر نمایش بصری تأثیر میگذارد (آیکون + در گوشه preview برای کپی، علامت ممنوعیت برای forbidden).
View.startDragAndDrop() — روش Android SDK برای شروع کشیدن، موجود از API 24 (Android 7.0). پس از گیرفتاری عنصر (معمولاً در onLongClick) فراخوان میشود. پارامترها: ClipData (داده)، DragShadowBuilder (سایه بصری)، Object myLocalState (وضعیت محلی)، flags. DragShadowBuilder یک Bitmap ایجاد میکند که انگشت را دنبال میکند. سیستم به طور آتوماتیک انیمیشن و رویدادهای لمسی را در طول کشیدن مدیریت میکند.
// Drag-and-Drop عنصر RecyclerView با تغییر ترتیب
class ReorderAdapter(private val items: MutableList<String>) :
RecyclerView.Adapter<ReorderAdapter.ViewHolder>() {
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.itemView.setOnLongClickListener {
// ClipData برای انتقال داده ایجاد میکنیم
val clipData = ClipData.newPlainText("item", items[position])
// DragShadowBuilder سفارشی با preview کوچکشده
val shadowBuilder = object : View.DragShadowBuilder(holder.itemView) {
override fun onProvideShadowMetrics(shadowSize: Point, shadowTouchPoint: Point) {
shadowSize.set(itemView.width, itemView.height / 2)
}
}
holder.itemView.startDragAndDrop(clipData, shadowBuilder, position, 0)
true
}
}
}پذیرش رهایش در Android از طریق View.setOnDragListener() انجام میشود. گوشکن رویدادهای ACTION_DRAG_STARTED، ACTION_DRAG_ENTERED، ACTION_DRAG_LOCATION، ACTION_DRAG_EXITED، ACTION_DROP، ACTION_DRAG_ENDED را دریافت میکند. در رویداد ACTION_DROP، دادهها از ClipData استخراج شده و عمل هدف انجام میشود. برای RecyclerView با تغییر ترتیب، ItemTouchHelper.SimpleCallback توصیه میشود — مکانیسم ساخته شده که به طور آتوماتیک Long Press را برای گیرفتاری، انیمیشن جابجایی عناصر و روشهای کمکی onMove/onSwiped را پردازش میکند.
تغییر ترتیب لیستها (drag to reorder) — رایجترین سناریوی DnD: کاربر عناصر لیست را برای تغییر ترتیب آنها کشیده و رها میکند. نمونهها: جابجایی مخاطبان در لیست علاقهمندیها، مرتب سازی وظایف در ریگبابهای پیگیری (Trello، Todoist)، تغییر ترتیب آهنگها در لیست پخش (Spotify، Apple Music). از طریق ItemTouchHelper در Android یا UILongPressGestureRecognizer + جابجایی سلولها در iOS پیاده سازی میشود.
مدیران فایل — Drag-and-Drop برای کپی و انتقال فایلها بین پوشهها. iOS Files App، Android Files by Google، Dropbox — همه DnD را پشتیبانی میکنند. کشیدن فایل روی پوشه آن را به داخل منتقل میکند (move)، کشیدن با کلید Option/Ctrl کپی میکند (copy). Drop target در حال نزدیک شدن به صورت بصری روشن شده و مرزهای پوشه را نشان میدهد.
تختههای کانبان — Trello، Jira، Notion از Drag-and-Drop برای انتقال کارتها بین ستونها (To Do → In Progress → Done) استفاده میکنند. در طول کشیدن، کارت از ستون مبدا جدا شده، انگشت را دنبال میکند و پس از عبور از مرز ستون دیگر با انیمیشن درج میشود. iOS 11+ از UIDragPreview با افکت پارالاکس پشتیبانی میکند که در آن preview کمی از نقطه لمس منحرف شده و عمق ایجاد میکند.
بخشهای Springboard (iOS) — کشیدن آیکونهای برنامه در صفحه اصلی iOS. فشار دراز روی آیکون حالت «jiggling» (لرزش آیکونها) را فعال میکند، سپس کاربر میتواند آیکونها را بین صفحات جابجا کرده و پوشهها ایجاد کند. این یکی از پیچیدهترین الگوهای UX DnD است چون نیازمند پردازش همزمان کشیدن، انیمیشن لرزش و گروهبندی در پوشهها است.
بازخورد بصری — برای Drag-and-Drop بسیار حیاتی است. شیء کشیده شده باید تغییر کند: کوچکتر شود (iOS: scale 0.8–0.9)، نیمه شفاف شود (alpha 0.7–0.8) و با سایه از رابط بالاتر برود (shadow offset). Drop target باید به نزدیک شدن واکنش بصری نشان دهد: روشن شود (highlight)، گسترش یابد (expand) یا مرزها را نشان دهد (border). بدون این سیگنالها، کاربر نمیداند شیء را کجا میتواند رها کند.
انیمیشن بازگشت (drag cancel) — اگر کاربر عنصر را خارج از منطقه رهایش یا در بالای منطقه نامعتبر رها کند، شیء باید با انیمیشن به مکان اصلی بازگردد. iOS UIDragInteractionDelegate روشهای dragInteraction(_:sessionDidMove:) برای پیگیری و dragInteraction(_:session:willEndWith:) برای انیمیشن بازگشت را فراهم میکند. Android View.setOnDragListener ACTION_DRAG_ENDED را دریافت میکند که در آن میتوان انیمیشن بازگشت را از طریق ValueAnimator یا ObjectAnimator آغاز کرد.
| توصیه | iOS | Android |
|---|---|---|
| گیرفتاری با حرکت | Long Press (پیشفرض) | OnLongClickListener |
| Drag preview | UIDragItem.previewProvider | DragShadowBuilder |
| بازخورد لامسی | UIImpactFeedbackGenerator | HapticFeedbackConstants.LONG_PRESS |
| انیمیشن بازگشت | UIDragPreviewTarget | ViewPropertyAnimator |
| Drop proposal | UIDropProposal (.move/.copy) | ACTION_DROP + فلگها |
| DnD بین برنامهای | iOS 11+ Split View (iPad) | Android 7.0+ Split Screen |
دسترسپذیری: کاربران با محدودیتهای حرکتی ممکن است نتوانند حرکت کشیدن را انجام دهند. از طریق دکمههای «حرکت به بالا/پایین» برای لیستها، منوی کتکستی «انتقال به پوشه» برای فایلها جایگزینهایی فراهم کنید. iOS VoiceOver از عمل روتر «Drag» برای جابجایی عناصر پشتیبانی میکند. Android TalkBack از طریق AccessibilityNodeInfo.addAction() اعمال سفارشی را فراهم میکند. Nike Run Club و Apple Health از DnD برای تغییر ترتیب متریکها در داشبورد استفاده میکنند — با دسترسی از طریق دکمههای استاندارد.
سوالات متداول
حرکت استاندارد برای شروع Drag-and-Drop Long Press (فشار دراز) به مدت 300–500 میلیثانیه است. iOS از مکانیسم ساخته UIDragInteraction استفاده میکند که به طور آتوماتیک لمس طولانی را به گیرفتاری تبدیل میکند. Android نیازمند فراخوان صریح startDragAndDrop() در فراخوان OnLongClickListener است. در بعضی موارد (مثلاً در بازیها)، DnD ممکن است با یک لمس ساده بدون تأخیر شروع شود.
بله، Drag-and-Drop بین برنامهای در iOS 11+ (iPad) و Android 7.0+ پشتیبانی میشود. در iPad، کشیدن در حالت Split View و Slide Over کار میکند — کاربر میتواند متنی از Safari گرفته و به یادداشتها منتقل کند. در Android، DnD بین برنامهها در Split Screen کار میکند. دادهها از طریق UIPasteConfiguration (iOS) یا ClipData (Android) با مشخص کردن نوعهای پشتیبانی شده (UTI، MIME) انتقال مییابند.
تعارض DnD و Scroll از طریق تأخیر در گیرفتاری حل میشود. اگر کاربر حرکت انگشت را کمتر از مهل زمانی Long Press (500 میلیثانیه) آغاز کند، حرکت به عنوان پیمایش تفسیر میشود نه Drag-and-Drop. در iOS، UIDragInteraction این تعارض را به طور آتوماتیک از طریق cancelsTouchesInView مدیریت میکند. در Android، ItemTouchHelper از آستانه استفاده میکند — اگر حرکت از آستانه TouchSlop تجاوز کند، Long Press به نفع پیمایش لغو میشود.
Drag-and-Drop از هر نوع دادهای پشتیبانی میکند: متن، تصاویر، فایلها، URL، اشیاء سفارشی. iOS از NSItemProvider با نوعهای UTI استفاده میکند (public.text، public.image، public.file-url). Android — ClipData با نوعهای MIME (text/plain، image/png، application/octet-stream). برای دادههای سفارشی، ClipData.Item با Intent ایجاد میشود. در DnD بین برنامهای، دادهها در قالبهای جهانی بسته میشوند: plain text، HTML، RTF، UIImage/PNG.
در iOS، انیمیشن بازگشت از طریق UIDragPreviewTarget انجام میشود — مکان نهایی که preview با انیمیشن به آن باز میگردد. در Android از ViewPropertyAnimator با translateX/translateY برای بازگرداندن عنصر به مکان اصلی استفاده کنید. زمان انیمیشن — 200–300 میلیثانیه با منحنی easing (EaseOut). UIKit به طور آتوماتیک UIDragPreview را با افکت پارالاکس اضافه میکند که در صورت لغو حرکت به طور صاف باز میگردد.
نتیجه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید