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) са колбеком 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)²). Тренутни scale се израчунава као однос тренутног растојања и почетног. Ако је почетно растојање било 100px, а постало 150px — scale = 1.5 (повећање од 50%). iOS преноси scale у односу на претходно стање (делта), 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 на контејнерски view, а не на појединачне елементе — то поједностављује израчунавање пивот тачке скалирања.

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:) — програмер не мора да израчунава пивот ручно. Android пружа focusX/focusY у ScaleGestureDetector, али при примени кроз Matrix пивот се преноси као трећи и четврти аргумент у postScale(scaleFactor, scaleFactor, focusX, focusY). Без пивот тачке, скалирање ће се вршити у односу на горњи леви угао (0,0), што доводи до померања садржаја при зуму.

По искуству IT Sectr, правилно израчунавање пивот тачке — најчешћи извор грешака при увођењу Pinch-to-Zoom. У 90% случајева довољно је користити уграђене координате GestureRecognizer и ScaleGestureDetector, без ручног израчунавања пивота. Ако се садржај помера при скалирању — проверите да ли преносите 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 кроз делегата. Ово је пожељан начин за слике и документе — 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 за све слике преко целог екрана — то 5 пута смањује обим кода у поређењу са ручном имплементацијом кроз UIPinchGestureRecognizer.

Често постављана питања

По чему се 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 — једноставније од ручне имплементације.
  • Обавезно ограничите размеру (0.5–3.0) кроз clamp — без тога садржај може постати невидљив.
  • Пивот тачка скалирања — центар између прстију; без ње садржај се помера при повећању.
  • У IT Sectr користимо UIScrollView за слике и прилагођени ScaleGestureDetector за едиторе — избор зависи од сложености трансформација.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође