Pinch-to-Zoom — ماهیت، پردازش حرکت نیشگون در برنامه‌های موبایل

نویسنده: IT Sectr منتشر شده: 2026-02-27 زمان مطالعه: 8 دقیقه
Pinch-to-Zoom (نیشگون انگشتان) — یک حرکت چندلمسی بزرگ‌نمایی است که کاربر دو انگشت خود را روی صفحه نمایش برای کوچک‌或 بزرگ‌نمایی محتوا جمع یا باز می‌کند. این حرکت به استاندارد واقعی برای مشاهده تصاویر، نقشه‌ها، صفحات وب و اسناد در دستگاه‌های موبایل تبدیل شده است. بر اساس Apple HIG و Google Material Design، Pinch-to-Zoom یک حرکت پایه‌ای است که کاربران در تمام برنامه‌های کار با محتوای بصری انتظار دارند.

نکات اصلی

  • Pinch-to-Zoom — یک حرکت چندلمسی با دو انگشت برای بزرگ‌نمایی محتوا در دستگاه‌های موبایل.
  • در iOS پردازش از طریق UIPinchGestureRecognizer با پیگیری پیوسته .began → .changed → .ended انجام می‌شود.
  • در Android از ScaleGestureDetector (از Support Library) با callback onScale برای به‌روزرسانی مقیاس از طریق Matrix استفاده می‌شود.
  • UIPinchGestureRecognizer دارای ویژگی scale (ضریب نسبت به حالت قبلی) و velocity (سرعت) است.
  • برای محدود کردن زوم از clamp ضریب scale بین مقادیر مشخص min/max (معمولاً 0.5–3.0) استفاده می‌شود.

Pinch-to-Zoom چیست؟

Pinch-to-Zoom (نیشگون یا فشار) — یک حرکت چندلمسی است که از دو انگشت برای تغییر مقیاس محتوای نمایش داده شده استفاده می‌کند. هنگام نزدیک کردن انگشتان (pinch close) مقیاس کاهش می‌یابد، هنگام باز کردن (pinch open) — افزایش می‌یابد. حرکت پیوسته است: سیستم فاصله بین انگشتان را در زمان واقعی ردیابی می‌کند و رویدادهای میانی تولید می‌کند و به برنامه اجازه می‌دهد مقیاس را به آرامی به‌روزرسانی کند.

مکانیک حرکت بر اساس محاسبه فاصله بین دو نقطه تماس است. iOS و Android از فاصله اقلیدسی استفاده می‌کنند: sqrt((x2-x1)² + (y2-y1)²). مقیاس فعلی به عنوان نسبت فاصله فعلی به فاصله اولیه محاسبه می‌شود. اگر فاصله اولیه 100px بود و 150px شد — scale = 1.5 (افزایش 50%). iOS scale را نسبت به حالت قبلی (delta) منتقل می‌کند، Android — scale انباشته شده را از طریق ScaleGestureDetector.

بر اساس Google Material Design Guidelines، Pinch-to-Zoom برای برنامه‌های نمایش تصاویر، نقشه‌ها یا اسناد الزامی است. در IT Sectr ما این حرکت را با UIPinchGestureRecognizer و ScaleGestureDetector در تمام پروژه‌های کار با محتوای بصری — از گالری‌ها تا ویرایشگرهای تصویر — پیاده‌سازی می‌کنیم.

Pinch-to-Zoom در iOS: UIPinchGestureRecognizer

UIPinchGestureRecognizer — زیرکلاس پیوسته UIGestureRecognizer برای تشخیص حرکت نیشگون. بر خلاف تشخیص‌دهنده‌های گسسته (UITapGestureRecognizer)، دنباله‌ای از رویدادها تولید می‌کند: .began (انگشتان صفحه را لمس کردند)، .changed (هر حرکت انگشتان)، .ended (انگشتان جدا شدند).

ویژگی‌های کلیدی: scale (CGFloat) — تغییر نسبی فاصله بین انگشتان از شروع حرکت؛ velocity (CGFloat) — سرعت تغییر scale در ثانیه. scale اولیه = 1.0. هنگام باز کردن انگشتان scale افزایش می‌یابد (1.0 → 1.5 → 2.0)، هنگام جمع کردن — کاهش می‌یابد (1.0 → 0.8 → 0.5). ویژگی velocity برای اینرسی استفاده می‌شود: پس از .ended می‌توان ادامه بزرگ‌نمایی را با کاهش تدریجی انیمیشن کرد.

UIPinchGestureRecognizer از ویژگی delegate برای کار همزمان با سایر تشخیص‌دهنده‌ها (مثلاً pan + pinch روی نقشه) پشتیبانی می‌کند. متد gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) true برمی‌گرداند و هر دو حرکت به صورت موازی کار می‌کنند. Apple توصیه می‌کند UIPinchGestureRecognizer را به نمای ظرف اضافه کنید نه به عناصر جداگانه — این کار محاسبه نقطه محوری بزرگ‌نمایی را ساده می‌کند.

Pinch-to-Zoom در Android: ScaleGestureDetector

ScaleGestureDetector — کلاسی از Android Support Library (android.view) که برای تشخیص بزرگ‌نمایی چندلمسی طراحی شده است. بر خلاف GestureDetector.onFling، ScaleGestureDetector به طور خاص برای زوم دو انگشتی طراحی شده و به طور خودکار focusX، focusY (مرکز بین انگشتان) و scaleFactor (دلتای مقیاس از رویداد قبلی) را محاسبه می‌کند.

ScaleGestureDetector از طریق رابط OnScaleGestureListener با سه متد کار می‌کند: onScaleBegin(detector) (معادل .began)، onScale(detector) (معادل .changed، boolean برمی‌گرداند — true اگر مقیاس پذیرفته شده)، onScaleEnd(detector) (معادل .ended). ویژگی‌ها: scaleFactor — ضریب مقیاس نسبت به رویداد قبلی (~1.01 در باز کردن آهسته)، focusX/focusY — مختصات مرکز بین انگشتان.

برای اعمال مقیاس به View معمولاً از Matrix استفاده می‌شود: view.imageMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY). Matrix از انباشت تبدیل‌ها پشتیبانی می‌کند — هر فراخوانی postScale یک تبدیل جدید به تبدیل موجود اضافه می‌کند. مهم است به خاطر داشته باشید: ScaleGestureDetector مقیاس حداقل/حداکثر را محدود نمی‌کند — این وظیفه توسعه‌دهنده است. در IT Sectr ما از ScaleGestureDetector با مرزهای سفارشی scale (0.5–3.0) و انیمیشن بازگشت در صورت تجاوز استفاده می‌کنیم.

محدودیت زوم و نقطه محوری

در پیاده‌سازی Pinch-to-Zoom محدود کردن مقیاس حداقل و حداکثر بسیار مهم است — بدون این کاربر می‌تواند تصویر را تا بی‌نهایت بزرگ کند یا تا نامرئی شدن کوچک کند. مقادیر استاندارد: min = 0.5 (50% اندازه اصلی)، max = 3.0 (300%). در iOS محدودیت از طریق clamp انجام می‌شود: newScale = min(max(currentScale * gesture.scale, minScale), maxScale). در Android — از طریق بررسی پس از postScale.

نقطه محوری — مرکز بین انگشتان که بزرگ‌نمایی نسبت به آن انجام می‌شود. iOS به طور خودکار مرکز دو لمس را محاسبه کرده و از طریق location(in:) منتقل می‌کند — توسعه‌دهنده نیازی به محاسبه دستی pivot ندارد. Android focusX/focusY را در ScaleGestureDetector فراهم می‌کند، اما هنگام اعمال از طریق Matrix pivot به عنوان آرگومان سوم و چهارم در postScale(scaleFactor, scaleFactor, focusX, focusY) منتقل می‌شود. بدون نقطه محوری، بزرگ‌نمایی نسبت به گوشه بالا سمت چپ (0,0) انجام می‌شود که منجر به جابجایی محتوا هنگام زوم می‌شود.

بر اساس تجربه IT Sectr، محاسبه صحیح نقطه محوری — رایج‌ترین منبع اشکال در پیاده‌سازی Pinch-to-Zoom است. در 90% موارد استفاده از مختصات داخلی GestureRecognizer و ScaleGestureDetector کافی است بدون محاسبه دستی pivot. اگر محتوا هنگام بزرگ‌نمایی جابجا می‌شود — بررسی کنید که focusX/focusY را به matrix.postScale() منتقل می‌کنید.

اینرسی پس از زوم

برای UX روان پس از اتمام حرکت می‌توان اینرسی اضافه کرد: در iOS — از velocity از UIPinchGestureRecognizer برای UIView.animate با پارامترهای spring استفاده کنید؛ در Android — OverScroller یا ValueAnimator. اینرسی نباید از مرزهای min/max scale خارج شود — مقدار نهایی با انیمیشن بازگشت spring در صورت تجاوز بریده می‌شود.

نمونه کد در Swift و Kotlin

مثال 1: Pinch-to-Zoom در iOS (Swift)

UIPinchGestureRecognizer را به UIImageView با محدودیت مقیاس از 0.5 تا 3.0 اضافه می‌کند. پیاده‌سازی حداقلی با حفظ مقیاس انباشته شده را نشان می‌دهد.

swift
import UIKit

class ZoomableImageViewController: UIViewController {

    @IBOutlet private var imageView: UIImageView!
    private var currentScale: CGFloat = 1.0
    private let minScale: CGFloat = 0.5
    private let maxScale: CGFloat = 3.0

    override func viewDidLoad() {
        super.viewDidLoad()

        let pinch = UIPinchGestureRecognizer(
            target: self,
            action: #selector(handlePinch(_:))
        )
        imageView.addGestureRecognizer(pinch)
        imageView.isUserInteractionEnabled = true
    }

    @objc private func handlePinch(_: UIPinchGestureRecognizer) {
        if gestureRecognizer.state == .began
            || gestureRecognizer.state == .changed {

            let newScale = currentScale * gestureRecognizer.scale
            imageView.transform = CGAffineTransform(
                scaleX: min(max(newScale, minScale), maxScale),
                y: min(max(newScale, minScale), maxScale)
            )
        }
        if gestureRecognizer.state == .ended {
            currentScale = min(max(
                currentScale * gestureRecognizer.scale,
                minScale
            ), maxScale)
            gestureRecognizer.scale = 1.0
        }
    }
}

منطق: در .began/.changed مقیاس از طریق CGAffineTransform با clamp اعمال می‌شود. در .ended scale به 1.0 بازنشانی می‌شود و مقدار انباشته شده در currentScale ذخیره می‌شود. این از «رانش» مقیاس در حرکات مکرر جلوگیری می‌کند — هر حرکت جدید از وضعیت فعلی شروع می‌شود. برای تصاویر به جای CGAffineTransform، UIScrollView با minimumZoomScale/maximumZoomScale توصیه می‌شود.

مثال 2: Pinch-to-Zoom در Android (Kotlin)

بزرگ‌نمایی ImageView را از طریق ScaleGestureDetector با Matrix پیاده‌سازی می‌کند. پردازش صحیح نقطه محوری و محدودیت مقیاس را نشان می‌دهد.

kotlin
import android.content.Context
import android.graphics.Matrix
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.widget.ImageView

class PinchZoomImageView(context: Context) : ImageView(context) {

    private val matrix = Matrix()
    private var currentScale = 1f
    private val minScale = 0.5f
    private val maxScale = 3.0f

    private val scaleDetector =
        ScaleGestureDetector(context, ScaleListener())

    private inner class ScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            currentScale = (currentScale * detector.scaleFactor)
                .coerceIn(minScale, maxScale)
            matrix.postScale(
                currentScale,
                currentScale,
                detector.focusX,
                detector.focusY
            )
            imageMatrix = matrix
            return true
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        scaleDetector.onTouchEvent(event)
        return true
    }
}

ImageView سفارشی onTouchEvent را بازنویسی می‌کند و رویدادها را به ScaleGestureDetector منتقل می‌کند. OnScaleListener.onScale() در هر حرکت انگشتان فراخوانی می‌شود. detector.scaleFactor — دلتا (1.01 در باز کردن آهسته). currentScale با coerceIn برای clamp انباشته می‌شود. focusX/focusY — مرکز بین انگشتان، برای بزرگ‌نمایی محوری صحیح به postScale منتقل می‌شود.

مثال 3: UIScrollView با پشتیبانی زوم (iOS)

UIScrollView دارای پشتیبانی داخلی Pinch-to-Zoom از طریق delegate است. این روش ترجیحی برای تصاویر و اسناد است — ScrollView مقیاس، اینرسی و پیمایش را به طور خودکار مدیریت می‌کند.

swift
class ScrollableImageViewController: UIViewController,
                                    UIScrollViewDelegate {

    @IBOutlet private var scrollView: UIScrollView!
    @IBOutlet private var imageView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.minimumZoomScale = 0.5
        scrollView.maximumZoomScale = 3.0
        scrollView.delegate = self
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
}

UIScrollView به طور مستقل UIPinchGestureRecognizer ایجاد می‌کند، مقیاس را از طریق تبدیل CALayer مدیریت می‌کند و هنگام تجاوز از مرزها اثر bounce اضافه می‌کند. کد به تنظیم min/maxZoomScale و بازگرداندن viewForZooming خلاصه می‌شود. در IT Sectr ما از UIScrollView برای تمام تصاویر تمام صفحه استفاده می‌کنیم — این حجم کد را در مقایسه با پیاده‌سازی دستی از طریق UIPinchGestureRecognizer 5 برابر کاهش می‌دهد.

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

pinch چه تفاوتی با نیشگون روی ترکپد دارد؟

در صفحه لمسی pinch از دو انگشت با ردیابی فاصله بین آنها استفاده می‌کند. در ترکپد MacBook بزرگ‌نمایی از طریق UIPinchGestureRecognizer با همان حالت‌ها (.began → .changed → .ended) پیاده‌سازی شده است، اما منبع لمس‌ها — ترکپد با Force Touch است نه صفحه خازنی. API یکسان است، مکانیک یکسان است.

چگونه زوم حداقل و حداکثر را محدود کنیم؟

در iOS: پس از دریافت scale از UIPinchGestureRecognizer در currentScale ضرب کنید و clamp(min: 0.5, max: 3.0) اعمال کنید. در Android: از طریق coerceIn(minScale, maxScale) قبل از matrix.postScale(). در UIScrollView — از طریق ویژگی‌های minimumZoomScale و maximumZoomScale. بدون محدودیت مقیاس می‌تواند به بی‌نهایت یا صفر برود.

آیا Pinch-to-Zoom با یک انگشت کار می‌کند؟

خیر، pinch حداقل به دو انگشت نیاز دارد. برای زوم تک انگشتی (double-tap + کشیدن به بالا/پایین) پیاده‌سازی سفارشی از طریق UIPanGestureRecognizer یا GestureDetector.onScroll لازم است. این یک حرکت متفاوت با منطق متفاوت است — معمولاً برای زوم در دوربین یا نقشه‌ها استفاده می‌شود.

خلاصه

  • Pinch-to-Zoom — یک حرکت چندلمسی با دو انگشت برای بزرگ‌نمایی محتوا، استاندارد برای تصاویر، نقشه‌ها و اسناد.
  • UIPinchGestureRecognizer در iOS — تشخیص‌دهنده پیوسته با scale (دلتا) و velocity (سرعت).
  • ScaleGestureDetector در Android — تشخیص‌دهنده تخصصی با onScale()، focusX/focusY برای نقطه محوری.
  • برای UIScrollView زوم از طریق minimumZoomScale/maximumZoomScale تعبیه می‌شود — ساده‌تر از پیاده‌سازی دستی.
  • حتماً مقیاس را از طریق clamp محدود کنید (0.5–3.0) — بدون این محتوا می‌تواند نامرئی شود.
  • نقطه محوری بزرگ‌نمایی — مرکز بین انگشتان؛ بدون آن محتوا هنگام بزرگ‌نمایی جابجا می‌شود.
  • در IT Sectr ما از UIScrollView برای تصاویر و ScaleGestureDetector سفارشی برای ویرایشگرها استفاده می‌کنیم — انتخاب به پیچیدگی تبدیل‌ها بستگی دارد.

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

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

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

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