Pinch-to-Zoom — essensen, hantering av nypgest i mobilappar

Författare: IT Sectr Publicerad: 2026-02-27 Lästid: 8 min
Pinch-to-Zoom (nypa med fingrarna) — en multi-touch zoomgest där användaren för ihop eller isär två fingrar på skärmen för att minska eller förstora innehållet. Denna gest har blivit de facto-standard för att visa bilder, kartor, webbsidor och dokument på mobila enheter. Enligt Apple HIG och Google Material Design är Pinch-to-Zoom den grundläggande gest som användare förväntar sig i alla appar som arbetar med visuellt innehåll.

Huvudpunkter

  • Pinch-to-Zoom — en multi-touch-gest med två fingrar för skalning av innehåll på mobila enheter.
  • I iOS utförs hanteringen via UIPinchGestureRecognizer med kontinuerlig spårning .began → .changed → .ended.
  • I Android används ScaleGestureDetector (från Support Library) med callback onScale för att uppdatera skalan via Matrix.
  • UIPinchGestureRecognizer har egenskaperna scale (koefficient jämfört med föregående tillstånd) och velocity (hastighet).
  • För att begränsa zoomen tillämpas clamp av scale-koefficienten mellan angivna min/max-värden (vanligtvis 0.5–3.0).

Vad är Pinch-to-Zoom?

Pinch-to-Zoom (nypa eller klämma) — en multi-touch-gest som använder två fingrar för att ändra skalan på visat innehåll. När fingrarna förs ihop (pinch close) minskar skalan, när de förs isär (pinch open) — ökar. Gesten är kontinuerlig: systemet spårar avståndet mellan fingrarna i realtid och genererar mellanhändelser, vilket gör att appen smidigt kan uppdatera skalan.

Gestens mekanik baseras på beräkning av avståndet mellan två beröringspunkter. iOS och Android använder euklidiskt avstånd: sqrt((x2-x1)² + (y2-y1)²). Aktuell scale beräknas som förhållandet mellan aktuellt avstånd och initialt avstånd. Om det initiala avståndet var 100px och blev 150px — scale = 1.5 (50% ökning). iOS skickar scale i förhållande till föregående tillstånd (delta), Android — ackumulerad scale via ScaleGestureDetector.

Enligt Google Material Design Guidelines är Pinch-to-Zoom obligatoriskt för appar som visar bilder, kartor eller dokument. På IT Sectr implementerar vi denna gest med UIPinchGestureRecognizer och ScaleGestureDetector i alla projekt som arbetar med visuellt innehåll — från gallerier till bildredigerare.

Pinch-to-Zoom i iOS: UIPinchGestureRecognizer

UIPinchGestureRecognizer — en kontinuerlig underklass till UIGestureRecognizer för att känna igen nypgesten. Till skillnad från diskreta igenkännare (UITapGestureRecognizer) genererar den en sekvens av händelser: .began (fingrarna vidrörde skärmen), .changed (varje fingerrörelse), .ended (fingrarna lyftes).

Nyckelegenskaper: scale (CGFloat) — relativ förändring av avståndet mellan fingrarna sedan gestens början; velocity (CGFloat) — hastighet för scale-förändring per sekund. Initial scale = 1.0. När fingrarna förs isär ökar scale (1.0 → 1.5 → 2.0), när de förs ihop — minskar (1.0 → 0.8 → 0.5). Egenskapen velocity används för tröghet: efter .ended kan man animera fortsatt skalning med dämpning.

UIPinchGestureRecognizer stöder egenskapen delegate för samtidig användning med andra igenkännare (t.ex. pan + pinch på karta). Metoden gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) returnerar true, och båda gesterna fungerar parallellt. Apple rekommenderar att lägga till UIPinchGestureRecognizer på en containervy, inte på enskilda element — detta förenklar beräkningen av skalningens pivotpunkt.

Pinch-to-Zoom i Android: ScaleGestureDetector

ScaleGestureDetector — en klass från Android Support Library (android.view), avsedd för att känna igen multi-touch-skalning. Till skillnad från GestureDetector.onFling är ScaleGestureDetector speciellt utformad för tvåfingerszoom och beräknar automatiskt focusX, focusY (mitten mellan fingrarna) och scaleFactor (delta av skala från föregående händelse).

ScaleGestureDetector fungerar via gränssnittet OnScaleGestureListener med tre metoder: onScaleBegin(detector) (motsvarighet till .began), onScale(detector) (motsvarighet till .changed, returnerar boolean — true om skalan accepteras), onScaleEnd(detector) (motsvarighet till .ended). Egenskaper: scaleFactor — multiplikator för skala jämfört med föregående händelse (~1.01 vid långsam isärföring), focusX/focusY — koordinater för mitten mellan fingrarna.

För att tillämpa skala på View används vanligtvis Matrix: view.imageMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY). Matrix stöder ackumulering av transformationer — varje anrop till postScale lägger till en ny transformation till den befintliga. Viktigt: ScaleGestureDetector begränsar inte minsta/största skala — det är utvecklarens uppgift. På IT Sectr tillämpar vi ScaleGestureDetector med anpassade skalgränser (0.5–3.0) och återgångsanimation vid överskridning.

Zoom-begränsning och pivotpunkt

Vid implementering av Pinch-to-Zoom är det kritiskt att begränsa minsta och största skala — utan detta kan användaren förstora bilden oändligt eller förminska den tills den blir osynlig. Standardvärden: min = 0.5 (50% av originalstorleken), max = 3.0 (300%). I iOS görs begränsningen via clamp: newScale = min(max(currentScale * gesture.scale, minScale), maxScale). I Android — via kontroll efter postScale.

Pivotpunkten — mitten mellan fingrarna i förhållande till vilken skalningen utförs. iOS beräknar automatiskt centrum för två beröringar och skickar det via location(in:) — utvecklaren behöver inte beräkna pivot manuellt. Android tillhandahåller focusX/focusY i ScaleGestureDetector, men vid tillämpning via Matrix skickas pivot som tredje och fjärde argument i postScale(scaleFactor, scaleFactor, focusX, focusY). Utan pivotpunkt kommer skalningen att utföras i förhållande till det övre vänstra hörnet (0,0), vilket leder till att innehållet förskjuts vid zoom.

Enligt IT Sectrs erfarenhet är korrekt beräkning av pivotpunkten den vanligaste källan till buggar vid implementering av Pinch-to-Zoom. I 90% av fallen räcker det att använda de inbyggda koordinaterna från GestureRecognizer och ScaleGestureDetector, utan att beräkna pivot manuellt. Om innehållet förskjuts vid skalning — kontrollera om du skickar focusX/focusY till matrix.postScale().

Tröghet efter zoom

För smidig användarupplevelse efter gestens slut kan tröghet läggas till: i iOS — använd velocity från UIPinchGestureRecognizer för UIView.animate med spring-parametrar; i Android — OverScroller eller ValueAnimator. Trögheten bör inte överskrida gränserna för min/max scale — slutvärdet kapas med spring-återgångsanimation vid överskridning.

Kodexempel i Swift och Kotlin

Exempel 1: Pinch-to-Zoom på iOS (Swift)

Lägger till UIPinchGestureRecognizer på UIImageView med skalbegränsning från 0.5 till 3.0. Visar en minimal implementering med bevarande av ackumulerad skala.

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

Logik: i .began/.changed tillämpas skalan via CGAffineTransform med clamp. I .edad återställs scale till 1.0 och det ackumulerade värdet sparas i currentScale. Detta förhindrar „drift“ av skalan vid flera gester — varje ny gest börjar från det aktuella tillståndet. För bilder rekommenderas UIScrollView med minimumZoomScale/maximumZoomScale istället för CGAffineTransform.

Exempel 2: Pinch-to-Zoom på Android (Kotlin)

Implementerar skalning av ImageView via ScaleGestureDetector med Matrix. Visar korrekt hantering av pivotpunkt och skalbegränsning.

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

Anpassad ImageView åsidosätter onTouchEvent och skickar händelser till ScaleGestureDetector. OnScaleListener.onScale() anropas vid varje fingerrörelse. detector.scaleFactor — delta (1.01 vid långsam isärföring). currentScale ackumuleras med coerceIn för clamp. focusX/focusY — mitten mellan fingrarna, skickas till postScale för korrekt pivotskalning.

Exempel 3: UIScrollView med zoomstöd (iOS)

UIScrollView har inbyggt stöd för Pinch-to-Zoom via delegat. Detta är den föredragna metoden för bilder och dokument — ScrollView hanterar skala, tröghet och rullning automatiskt.

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 skapar självständigt UIPinchGestureRecognizer, hanterar skala via CALayer-transformation och lägger till en bounce-effekt vid överskridning av gränser. Koden reduceras till inställning av min/maxZoomScale och returnering av viewForZooming. På IT Sectr använder vi UIScrollView för alla bilder i fullskärm — detta minskar kodmängden med 5 gånger jämfört med manuell implementering via UIPinchGestureRecognizer.

Vanliga frågor

Vad skiljer pinch från nypning på styrplattan?

På en pekskärm använder pinch två fingrar med spårning av avståndet mellan dem. På en MacBook-styrplatta implementeras skalning via UIPinchGestureRecognizer med samma tillstånd (.began → .changed → .ended), men källan till beröringarna är styrplattan med Force Touch, inte en kapacitiv skärm. API:et är detsamma, mekaniken identisk.

Hur begränsar man minsta och största zoom?

I iOS: efter att ha fått scale från UIPinchGestureRecognizer, multiplicera med currentScale och tillämpa clamp(min: 0.5, max: 3.0). I Android: via coerceIn(minScale, maxScale) före matrix.postScale(). I UIScrollView — via egenskaperna minimumZoomScale och maximumZoomScale. Utan begränsning kan skalan gå till oändlighet eller noll.

Fungerar Pinch-to-Zoom med ett finger?

Nej, pinch kräver minst två fingrar. För zoom med ett finger (double-tap + dra uppåt/nedåt) krävs en anpassad implementering via UIPanGestureRecognizer eller GestureDetector.onScroll. Detta är en annan gest med annan logik — vanligtvis används för zoom i kamera eller kartor.

Sammanfattning

  • Pinch-to-Zoom — en multi-touch-gest med två fingrar för skalning av innehåll, standard för bilder, kartor och dokument.
  • UIPinchGestureRecognizer i iOS — kontinuerlig igenkännare med scale (delta) och velocity (hastighet).
  • ScaleGestureDetector i Android — specialiserad detektor med onScale(), focusX/focusY för pivotpunkt.
  • För UIScrollView bäddas zoom in via minimumZoomScale/maximumZoomScale — enklare än manuell implementering.
  • Begränsa alltid skalan (0.5–3.0) via clamp — utan detta kan innehållet bli osynligt.
  • Skalningens pivotpunkt — mitten mellan fingrarna; utan den förskjuts innehållet vid förstoring.
  • På IT Sectr använder vi UIScrollView för bilder och anpassad ScaleGestureDetector för redigerare — valet beror på transformationernas komplexitet.

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också