Gesture Recognizer — چیست، پردازش لمس در iOS

نویسنده: IT Sectr منتشر شده: 2026-02-27 زمان مطالعه: 8 دقیقه
Gesture Recognizer — انتزاعی در UIKit است که دنباله لمس‌های کاربر را به یک ژست شناسایی‌شده تبدیل می‌کند: ضربه، کشیدن، فشار طولانی، نیشگون یا چرخش. به جای ردیابی دستی مختصات در متدهای touchesBegan/touchesMoved، توسعه‌دهنده یک شیء آماده UIGestureRecognizer به UIView اضافه می‌کند و متد action را پیاده‌سازی می‌کند. بر اساس Apple Developer Documentation، UIGestureRecognizer از 7 زیرکلاس داخلی پشتیبانی می‌کند که 95٪ از ژست‌های معمولی در برنامه‌های موبایل را پوشش می‌دهد.

نکات اصلی

  • UIGestureRecognizer — کلاس انتزاعی UIKit برای تشخیص الگوهای لمس: ضربه‌ها، کشیدن‌ها، نیشگون‌ها، چرخش‌ها و فشارهای طولانی.
  • 7 زیرکلاس داخلی: UITapGestureRecognizer, UISwipeGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UILongPressGestureRecognizer, UIPanGestureRecognizer, UIScreenEdgePanGestureRecognizer.
  • هر تشخیص‌دهنده وضعیتی دارد (possible, began, changed, ended, cancelled, failed) که با حرکت انگشتان به‌روز می‌شود.
  • برای حل تعارضات بین ژست‌ها از متد require(toFail:) استفاده می‌شود که اولویت تشخیص را تعیین می‌کند.
  • Gesture Recognizer با UIKit و UIViewRepresentable در SwiftUI کار می‌کند، اما در SwiftUI بومی از اصلاح‌کننده‌های ژست استفاده می‌شود.

Gesture Recognizer چیست؟

UIGestureRecognizer — کلاس پایه انتزاعی در UIKit است که فرآیند تشخیص ژست را به اشیاء جداگانه تجزیه می‌کند. هر زیرکلاس مسئول یک نوع ژست است: UITapGestureRecognizer — برای ضربه با تعداد مشخصی انگشت، UISwipeGestureRecognizer — برای کشیدن در جهت مشخص. توسعه‌دهنده تشخیص‌دهنده را از طریق متد addGestureRecognizer(:) به UIView اضافه می‌کند و UIKit به طور خودکار لمس‌ها را ردیابی می‌کند، وضعیت را به‌روز می‌کند و در صورت تشخیص، action را فراخوانی می‌کند.

قبل از ظهور UIGestureRecognizer (iOS 3.2, 2010) توسعه‌دهندگان متدهای UIResponder — touchesBegan, touchesMoved, touchesEnded — را بازنویسی می‌کردند و به صورت دستی مسیر لمس‌ها را تحلیل می‌کردند. این منجر به تکرار کد و خطاها در پردازش لمس‌های همزمان می‌شد. Apple این منطق را در UIGestureRecognizer کپسوله کرد و پشتیبانی از مولتی‌تاچ، لغو ژست‌ها و کار همزمان چندین تشخیص‌دهنده روی یک ویو را اضافه کرد.

طبق جلسات WWDC، Gesture Recognizer تا 11 لمس همزمان در iPad و 5 در iPhone را پردازش می‌کند. در IT Sectr ما از UIGestureRecognizer به عنوان روش استاندارد پردازش ورودی کاربر در تمام پروژه‌های UIKit استفاده می‌کنیم — این کار باگ‌های مربوط به ردیابی دستی touchesBegan را حذف کرده است.

وضعیت‌های UIGestureRecognizer

هر UIGestureRecognizer از 7 وضعیت ممکن عبور می‌کند که در enum UIGestureRecognizer.State تعریف شده‌اند. این وضعیت‌ها چرخه حیات تشخیص را منعکس می‌کنند: از تشخیص لمس تا تکمیل یا لغو ژست. درک وضعیت‌ها برای پیاده‌سازی تشخیص‌دهنده‌های سفارشی و رفع اشکال تعارضات حیاتی است.

وضعیت معنی زمان وقوع
.possible وضعیت اولیه، ژست هنوز تشخیص داده نشده بلافاصله پس از اضافه شدن به ویو
.began ژست تشخیص داده شد و شروع به اجرا شد در اولین حرکت انگشت برای pan/longPress
.changed پارامترهای ژست تغییر کردند (مختصات، زاویه) در هر حرکت انگشت
.ended کاربر انگشت را برداشت، ژست کامل شد در touchesEnded
.cancelled ژست توسط سیستم قطع شد (تماس ورودی، تغییر جهت) در touchesCancelled
.failed ژست با توجه به شرایط تشخیص داده نشد در touchesCancelled بدون تشخیص
.recognized مترادف .ended; ژست با موفقیت تشخیص داده شد همان .ended

ژست‌های گسسته (tap, swipe) از .possible مستقیماً به .ended یا .failed می‌روند. ژست‌های پیوسته (pan, pinch, rotation, longPress) از .possible → .began → .changed (چندبار) → .ended عبور می‌کنند. در متد action، gestureRecognizer.state را بررسی کنید — این امکان تشخیص شروع، تغییر و پایان ژست را می‌دهد.

انواع ژست‌ها در iOS

UIKit 7 زیرکلاس داخلی UIGestureRecognizer را ارائه می‌دهد که اکثر سناریوهای تعامل را پوشش می‌دهد. هر زیرکلاس تنظیمات خاصی دارد: numberOfTapsRequired برای ضربه، direction برای کشیدن، minimumPressDuration برای فشار طولانی.

  • UITapGestureRecognizer — ضربه‌های تکی و چندتایی را تشخیص می‌دهد. تعداد ضربه‌ها (numberOfTapsRequired) و انگشتان (numberOfTouchesRequired) قابل تنظیم است. برای دکمه‌ها، لینک‌ها، انتخاب عناصر استفاده می‌شود.
  • UISwipeGestureRecognizer — کشیدن در یکی از چهار جهت (right, left, up, down) را تشخیص می‌دهد. ویژگی direction جهت را تعیین می‌کند؛ numberOfTouchesRequired — تعداد انگشتان.
  • UIPanGestureRecognizer — ژست پیوسته برای کشیدن. متد translation(in:) جابجایی از نقطه شروع را برمی‌گرداند. برای drag-and-drop، کاروسل‌ها، pull-to-refresh استفاده می‌شود.
  • UIPinchGestureRecognizer — بزرگنمایی با دو انگشت. ویژگی scale ضریب مقیاس فعلی را منعکس می‌کند. در مقاله Pinch-to-Zoom به تفصیل بررسی شده است.
  • UIRotationGestureRecognizer — چرخش با دو انگشت. ویژگی rotation زاویه را بر حسب رادیان ذخیره می‌کند. برای چرخش تصاویر، نقشه‌ها، بوم‌ها استفاده می‌شود.
  • UILongPressGestureRecognizer — فشار طولانی. پارامترهای minimumPressDuration (ثانیه)، allowableMovement (پیکسل). برای منوهای زمینه، کشیدن عناصر استفاده می‌شود.
  • UIScreenEdgePanGestureRecognizer — پانوراما از لبه صفحه. ویژگی edges مشخص می‌کند کدام لبه ردیابی شود. برای ژست‌های ناوبری، پرده‌های اعلان استفاده می‌شود.

برای ژست‌های سفارشی (مثلاً رسم زیگزاگ) زیرکلاسی از UIGestureRecognizer با بازنویسی متدهای touchesBegan, touchesMoved, touchesEnded و به‌روزرسانی state ایجاد می‌شود. Apple توصیه می‌کند تا حد امکان از کلاس‌های داخلی استفاده کنید — آنها بهینه شده‌اند و به درستی با یکدیگر تعامل دارند.

حل تعارضات بین ژست‌ها

وقتی روی یک UIView از چند UIGestureRecognizer استفاده می‌شود (مثلاً tap و double-tap)، تعارض تشخیص رخ می‌دهد: در ضربه دوبل، ابتدا ضربه تکی فعال می‌شود. برای حل این مشکل، Apple متد require(toFail:) را ارائه می‌دهد که تشخیص یک ژست را تا زمانی که ژست دیگر با شکست مواجه شود، به تأخیر می‌اندازد.

مکانیزم به این صورت کار می‌کند: با فراخوانی tapRecognizer.require(toFail: doubleTapRecognizer)، مشخص می‌کنید که tapRecogniser فقط پس از آنکه doubleTapRecogniser در .failed تمام شود، به وضعیت .recognized می‌رود. این یک تأخیر ~0.3 ثانیه قبل از اجرای ضربه تکی اضافه می‌کند — کاربر دو بار ضربه می‌زند و اولین ضربه نادیده گرفته می‌شود. روش جایگزین — delegate UIGestureRecognizerDelegate با متد gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) که تشخیص همزمان را مجاز می‌کند (مثلاً pan + pinch برای نقشه).

طبق WWDC 2020، حدود 15٪ از باگ‌ها در برنامه‌های UIKit به پیکربندی نادرست تعارضات ژست مرتبط هستند. در IT Sectr ما رویکرد را استاندارد کرده‌ایم: برای هر صفحه یک طرح ژست با اولویت‌های require(toFail:) ایجاد می‌شود — این کار باگ‌های فعال شدن دوبل ضربه‌ها را کاملاً حذف کرده است.

مثال‌های کد در Swift

مثال 1: UITapGestureRecognizer — ضربه روی تصویر

یک handler ضربه تکی روی UIImageView اضافه می‌کند. هنگام ضربه، تصویر شفافیت خود را تغییر می‌دهد — ساده‌ترین مثال که اتصال ژست به ویو را نشان می‌دهد.

swift
import UIKit

class ImageViewController: UIViewController {

    @IBOutlet private var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        let tap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleTap(_:))
        )
        tap.numberOfTapsRequired = 1
        imageView.addGestureRecognizer(tap)
        imageView.isUserInteractionEnabled = true
    }

    @objc private func handleTap(_: UITapGestureRecognizer) {
        UIView.animate(withDuration: 0.2) {
            self.imageView.alpha = self.imageView.alpha == 1.0 ? 0.5 : 1.0
        }
    }
}

نکته کلیدی: isUserInteractionEnabled در UIImageView به طور پیش‌فرض false است — بدون این پرچم Gesture Recognizer لمس‌ها را دریافت نمی‌کند. برای UIView و UIButton این پرچم به طور پیش‌فرض فعال است. متد action پارامتر UITapGestureRecognizer را می‌پذیرد که از طریق آن می‌توان location(in:) را برای تعیین مختصات ضربه دریافت کرد.

مثال 2: UISwipeGestureRecognizer — کشیدن برای ناوبری

کشیدن به چپ برای بازگشت به صفحه قبلی را پیاده‌سازی می‌کند. تنظیم جهت و اتصال ژست به ویو ریشه کنترلر را نشان می‌دهد.

swift
import UIKit

class DetailViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let swipeLeft = UISwipeGestureRecognizer(
            target: self,
            action: #selector(handleSwipe(_:))
        )
        swipeLeft.direction = .left
        view.addGestureRecognizer(swipeLeft)
    }

    @objc private func handleSwipe(_: UISwipeGestureRecognizer) {
        navigationController?.popViewController(animated: true)
    }
}

UISwipeGestureRecognizer — یک ژست گسسته است: بلافاصله پس از تشخیص به .recognised می‌رود، بدون .changed میانی. بنابراین در action نیازی به بررسی state نیست — ژست یا تشخیص داده می‌شود (action فراخوانی می‌شود) یا نه. ویژگی direction یکی از چهار مقدار را می‌پذیرد: .left, .right, .up, .down. برای پشتیبانی از چندین جهت، تشخیص‌دهنده‌های جداگانه برای هر کدام ایجاد کنید.

مثال 3: ترکیب ژست‌ها با require(toFail:)

نشان می‌دهد چگونه ضربه تکی و دوبل را روی یک ویو بدون تعارض تنظیم کنید. تشخیص‌دهنده ضربه دوبل اولویت دارد — ضربه تکی فقط در صورتی فعال می‌شود که ضربه دوبل تشخیص داده نشود.

swift
import UIKit

class TapViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let singleTap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleSingleTap)
        )
        singleTap.numberOfTapsRequired = 1

        let doubleTap = UITapGestureRecognizer(
            target: self,
            action: #selector(handleDoubleTap)
        )
        doubleTap.numberOfTapsRequired = 2

        singleTap.require(toFail: doubleTap)

        view.addGestureRecognizer(singleTap)
        view.addGestureRecognizer(doubleTap)
    }

    @objc private func handleSingleTap() {
        print("Single tap — after 0.3s delay")
    }

    @objc private func handleDoubleTap() {
        print("Double tap — instant")
    }
}

بدون require(toFail:) در ضربه دوبل، ابتدا handleSingleTap و سپس handleDoubleTap فراخوانی می‌شود — این UX را خراب می‌کند. با require(toFail:) ضربه تکی ~0.3 ثانیه صبر می‌کند تا مطمئن شود ضربه دوم نمی‌آید. در IT Sectr این الگو در ویرایشگرهای تصویر و گالری‌ها استفاده می‌شود، جایی که ضربه دوبل بزرگنمایی می‌کند و ضربه تکی عنصر را انتخاب می‌کند.

سوالات متداول

آیا می‌توان از چند Gesture Recognizer روی یک ویو استفاده کرد؟

بله، UIView همزمان از چندین UIGestureRecognizer پشتیبانی می‌کند. برای حل تعارضات از متد require(toFail:) استفاده کنید که ترتیب تشخیص را تعیین می‌کند. برای کار موازی ژست‌ها (مثلاً pan + pinch روی نقشه) delegate gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) را پیاده‌سازی کنید که true برمی‌گرداند.

UIGestureRecognizer چه تفاوتی با touchesBegan دارد؟

UIGestureRecognizer — انتزاع سطح بالا که الگوهای لمس را به طور خودکار تشخیص می‌دهد و وضعیت‌ها را مدیریت می‌کند. touchesBegan — متد سطح پایین UIResponder که نیاز به ردیابی دستی مختصات، زمان‌بندی و لغو لمس‌ها دارد. Gesture Recognizer ساده‌تر، قابل اعتمادتر و برای ژست‌های استاندارد ترجیح داده می‌شود؛ touchesBegan فقط برای گرافیک سفارشی توجیه دارد.

آیا Gesture Recognizer در SwiftUI کار می‌کند؟

در SwiftUI بومی از اصلاح‌کننده‌ها استفاده می‌شود: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. این‌ها معادل‌های اعلامی UIGestureRecognizer هستند که در سلسله‌مراتب SwiftUI ادغام شده‌اند. در صورت نیاز می‌توان تشخیص‌دهنده UIKit را از طریق UIViewRepresentable پیچید، اما Apple استفاده از ژست‌های بومی SwiftUI را توصیه می‌کند.

خلاصه

  • UIGestureRecognizer — انتزاع UIKit برای تجزیه منطق تشخیص ژست به اشیاء جداگانه با مدیریت خودکار وضعیت‌ها.
  • 7 زیرکلاس داخلی 95٪ ژست‌های معمولی را پوشش می‌دهد: ضربه، کشیدن، پانوراما، نیشگون، چرخش، فشار طولانی و کشیدن از لبه.
  • هر تشخیص‌دهنده از 7 وضعیت عبور می‌کند — از .possible تا .ended/.failed که امکان ردیابی دقیق ژست‌های گسسته و پیوسته را فراهم می‌کند.
  • متد require(toFail:) تعارضات بین ژست‌ها را در یک ویو حل می‌کند و اولویت تشخیص را تعیین می‌کند.
  • در SwiftUI از اصلاح‌کننده‌های اعلامی ژست استفاده می‌شود، اما تشخیص‌دهنده‌های UIKit از طریق UIViewRepresentable قابل دسترسی هستند.
  • Gesture Recognizer برای پروژه‌های UIKit ضروری است؛ touchesBegan فقط برای طراحی سفارشی و گرافیک سطح پایین توجیه دارد.
  • از require(toFail:) برای جفت‌های tap/double-tap و gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) برای ژست‌های موازی استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید