Pinch-to-Zoom — esensi, pemrosesan gerakan mencubit di aplikasi seluler

Penulis: IT Sectr Diterbitkan: 2026-02-27 Waktu membaca: 8 mnt
Pinch-to-Zoom (cubitan jari) — gerakan penskalaan multi-sentuh di mana pengguna menyatukan atau merenggangkan dua jari di layar untuk memperkecil atau memperbesar konten. Gerakan ini telah menjadi standar de facto untuk melihat gambar, peta, halaman web, dan dokumen di perangkat seluler. Menurut Apple HIG dan Google Material Design, Pinch-to-Zoom adalah gerakan dasar yang diharapkan pengguna di semua aplikasi yang bekerja dengan konten visual.

Poin utama

  • Pinch-to-Zoom — gerakan multi-sentuh dengan dua jari untuk menskalakan konten di perangkat seluler.
  • Di iOS, pemrosesan dilakukan melalui UIPinchGestureRecognizer dengan pelacakan berkelanjutan .began → .changed → .ended.
  • Di Android, digunakan ScaleGestureDetector (dari Support Library) dengan callback onScale untuk memperbarui skala melalui Matrix.
  • UIPinchGestureRecognizer memiliki properti scale (koefisien relatif terhadap keadaan sebelumnya) dan velocity (kecepatan).
  • Untuk membatasi zoom, clamp koefisien scale diterapkan antara nilai min/max yang ditentukan (biasanya 0.5–3.0).

Apa itu Pinch-to-Zoom?

Pinch-to-Zoom (cubitan atau tekanan) — gerakan multi-sentuh yang menggunakan dua jari untuk mengubah skala konten yang ditampilkan. Saat menyatukan jari (pinch close) skala mengecil, saat merenggangkan (pinch open) — membesar. Gerakan ini berkelanjutan: sistem melacak jarak antara jari secara real-time dan menghasilkan peristiwa antara, memungkinkan aplikasi memperbarui skala dengan mulus.

Mekanika gerakan didasarkan pada perhitungan jarak antara dua titik sentuhan. iOS dan Android menggunakan jarak Euclidean: sqrt((x2-x1)² + (y2-y1)²). Skala saat ini dihitung sebagai rasio jarak saat ini terhadap jarak awal. Jika jarak awal adalah 100px dan menjadi 150px — scale = 1.5 (peningkatan 50%). iOS mengirimkan scale relatif terhadap keadaan sebelumnya (delta), Android — scale terakumulasi melalui ScaleGestureDetector.

Menurut Google Material Design Guidelines, Pinch-to-Zoom wajib untuk aplikasi yang menampilkan gambar, peta, atau dokumen. Di IT Sectr, kami menerapkan gerakan ini dengan UIPinchGestureRecognizer dan ScaleGestureDetector di semua proyek yang bekerja dengan konten visual — dari galeri hingga editor gambar.

Pinch-to-Zoom di iOS: UIPinchGestureRecognizer

UIPinchGestureRecognizer — subkelas berkelanjutan dari UIGestureRecognizer untuk mengenali gerakan mencubit. Berbeda dengan pengenal diskrit (UITapGestureRecognizer), ia menghasilkan urutan peristiwa: .began (jari menyentuh layar), .changed (setiap gerakan jari), .ended (jari diangkat).

Properti utama: scale (CGFloat) — perubahan relatif jarak antara jari dari awal gerakan; velocity (CGFloat) — kecepatan perubahan scale per detik. Scale awal = 1.0. Saat merenggangkan jari, scale meningkat (1.0 → 1.5 → 2.0), saat menyatukan — menurun (1.0 → 0.8 → 0.5). Properti velocity digunakan untuk inersia: setelah .ended, dapat menganimasikan kelanjutan penskalaan dengan redaman.

UIPinchGestureRecognizer mendukung properti delegate untuk bekerja secara bersamaan dengan pengenal lain (misalnya, pan + pinch di peta). Metode gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) mengembalikan true, dan kedua gerakan bekerja secara paralel. Apple merekomendasikan menambahkan UIPinchGestureRecognizer ke view wadah, bukan ke elemen individual — ini menyederhanakan perhitungan titik pivot penskalaan.

Pinch-to-Zoom di Android: ScaleGestureDetector

ScaleGestureDetector — kelas dari Android Support Library (android.view), dirancang untuk mengenali penskalaan multi-sentuh. Berbeda dengan GestureDetector.onFling, ScaleGestureDetector dirancang khusus untuk zoom dua jari dan secara otomatis menghitung focusX, focusY (pusat antara jari) dan scaleFactor (delta skala dari peristiwa sebelumnya).

ScaleGestureDetector bekerja melalui antarmuka OnScaleGestureListener dengan tiga metode: onScaleBegin(detector) (setara .began), onScale(detector) (setara .changed, mengembalikan boolean — true jika skala diterima), onScaleEnd(detector) (setara .ended). Properti: scaleFactor — pengali skala relatif terhadap peristiwa sebelumnya (~1.01 saat merenggangkan lambat), focusX/focusY — koordinat pusat antara jari.

Untuk menerapkan skala ke View, biasanya menggunakan Matrix: view.imageMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY). Matrix mendukung akumulasi transformasi — setiap panggilan postScale menambahkan transformasi baru ke transformasi yang ada. Penting untuk diingat: ScaleGestureDetector tidak membatasi skala minimum/maksimum — ini tugas pengembang. Di IT Sectr, kami menerapkan ScaleGestureDetector dengan batas skala kustom (0.5–3.0) dan animasi kembali saat melampaui batas.

Batasan zoom dan titik pivot

Dalam implementasi Pinch-to-Zoom, sangat penting untuk membatasi skala minimum dan maksimum — tanpa ini, pengguna dapat memperbesar gambar hingga tak terbatas atau memperkecil hingga tidak terlihat. Nilai standar: min = 0.5 (50% dari ukuran asli), max = 3.0 (300%). Di iOS, pembatasan dilakukan melalui clamp: newScale = min(max(currentScale * gesture.scale, minScale), maxScale). Di Android — melalui pemeriksaan setelah postScale.

Titik pivot — pusat antara jari tempat penskalaan dilakukan. iOS secara otomatis menghitung pusat dua sentuhan dan mengirimkannya melalui location(in:) — pengembang tidak perlu menghitung pivot secara manual. Android menyediakan focusX/focusY di ScaleGestureDetector, tetapi saat menerapkan melalui Matrix, pivot dikirimkan sebagai argumen ketiga dan keempat dalam postScale(scaleFactor, scaleFactor, focusX, focusY). Tanpa titik pivot, penskalaan akan dilakukan relatif terhadap sudut kiri atas (0,0), yang menyebabkan pergeseran konten saat zoom.

Menurut pengalaman IT Sectr, perhitungan titik pivot yang benar — sumber bug paling umum dalam implementasi Pinch-to-Zoom. Dalam 90% kasus, cukup menggunakan koordinat bawaan GestureRecognizer dan ScaleGestureDetector, tanpa menghitung pivot secara manual. Jika konten bergeser saat penskalaan — periksa apakah Anda mengirimkan focusX/focusY ke matrix.postScale().

Inersia setelah zoom

Untuk UX yang mulus setelah gerakan selesai, dapat ditambahkan inersia: di iOS — gunakan velocity dari UIPinchGestureRecognizer untuk UIView.animate dengan parameter spring; di Android — OverScroller atau ValueAnimator. Inersia tidak boleh melampaui batas min/max scale — nilai akhir dipotong dengan animasi kembali spring saat melampaui batas.

Contoh kode di Swift dan Kotlin

Contoh 1: Pinch-to-Zoom di iOS (Swift)

Menambahkan UIPinchGestureRecognizer ke UIImageView dengan batasan skala dari 0.5 hingga 3.0. Menunjukkan implementasi minimal dengan mempertahankan skala terakumulasi.

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
        }
    }
}

Logika: di .began/.changed, skala diterapkan melalui CGAffineTransform dengan clamp. Di .ended, scale direset ke 1.0 dan nilai terakumulasi disimpan di currentScale. Ini mencegah "drift" skala saat beberapa gerakan — setiap gerakan baru dimulai dari keadaan saat ini. Untuk gambar, daripada CGAffineTransform, direkomendasikan UIScrollView dengan minimumZoomScale/maximumZoomScale.

Contoh 2: Pinch-to-Zoom di Android (Kotlin)

Mengimplementasikan penskalaan ImageView melalui ScaleGestureDetector dengan Matrix. Menunjukkan pemrosesan titik pivot yang benar dan batasan skala.

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 kustom mengganti onTouchEvent, meneruskan peristiwa ke ScaleGestureDetector. OnScaleListener.onScale() dipanggil pada setiap gerakan jari. detector.scaleFactor — delta (1.01 saat merenggangkan lambat). currentScale terakumulasi dengan coerceIn untuk clamp. focusX/focusY — pusat antara jari, diteruskan ke postScale untuk penskalaan pivot yang benar.

Contoh 3: UIScrollView dengan dukungan zoom (iOS)

UIScrollView memiliki dukungan bawaan untuk Pinch-to-Zoom melalui delegasi. Ini adalah metode yang lebih disukai untuk gambar dan dokumen — ScrollView mengelola skala, inersia, dan pengguliran secara otomatis.

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 secara mandiri membuat UIPinchGestureRecognizer, mengelola skala melalui transformasi CALayer, dan menambahkan efek bounce saat melampaui batas. Kode direduksi menjadi pengaturan min/maxZoomScale dan mengembalikan viewForZooming. Di IT Sectr, kami menggunakan UIScrollView untuk semua gambar layar penuh — ini mengurangi volume kode 5 kali lipat dibandingkan dengan implementasi manual melalui UIPinchGestureRecognizer.

Pertanyaan yang sering diajukan

Apa perbedaan pinch dengan cubitan di trackpad?

Di layar sentuh, pinch menggunakan dua jari dengan pelacakan jarak di antara mereka. Di trackpad MacBook, penskalaan diimplementasikan melalui UIPinchGestureRecognizer dengan keadaan yang sama (.began → .changed → .ended), tetapi sumber sentuhan adalah trackpad dengan Force Touch, bukan layar kapasitif. API-nya sama, mekanikanya identik.

Bagaimana cara membatasi zoom minimum dan maksimum?

Di iOS: setelah menerima scale dari UIPinchGestureRecognizer, kalikan dengan currentScale dan terapkan clamp(min: 0.5, max: 3.0). Di Android: melalui coerceIn(minScale, maxScale) sebelum matrix.postScale(). Di UIScrollView — melalui properti minimumZoomScale dan maximumZoomScale. Tanpa batasan, skala bisa menjadi tak terbatas atau nol.

Apakah Pinch-to-Zoom berfungsi dengan satu jari?

Tidak, pinch membutuhkan setidaknya dua jari. Untuk zoom satu jari (double-tap + seret ke atas/bawah), diperlukan implementasi kustom melalui UIPanGestureRecognizer atau GestureDetector.onScroll. Ini adalah gerakan yang berbeda dengan logika yang berbeda — biasanya digunakan untuk zoom di kamera atau peta.

Ringkasan

  • Pinch-to-Zoom — gerakan multi-sentuh dengan dua jari untuk menskalakan konten, standar untuk gambar, peta, dan dokumen.
  • UIPinchGestureRecognizer di iOS — pengenal berkelanjutan dengan scale (delta) dan velocity (kecepatan).
  • ScaleGestureDetector di Android — detektor khusus dengan onScale(), focusX/focusY untuk titik pivot.
  • Untuk UIScrollView, zoom dipasang melalui minimumZoomScale/maximumZoomScale — lebih sederhana daripada implementasi manual.
  • Wajib batasi skala (0.5–3.0) melalui clamp — tanpa ini konten bisa menjadi tidak terlihat.
  • Titik pivot penskalaan — pusat antara jari; tanpa itu konten bergeser saat diperbesar.
  • Di IT Sectr, kami menggunakan UIScrollView untuk gambar dan ScaleGestureDetector kustom untuk editor — pilihan tergantung pada kompleksitas transformasi.

Kami akan mengembangkan aplikasi seluler turnkey

IT Sectr membuat aplikasi iOS dan Android untuk startup dan bisnis sejak 2017. Kami akan memberi saran dan mengusulkan solusi terbaik.

Diskusikan proyek

Baca juga