Anahtar Noktalar
Pinch-to-Zoom (kıstırma), görüntülenen içeriğin ölçeğini değiştirmek için iki parmak kullanan çoklu dokunmatik bir harekettir. Parmaklar birbirine yaklaştırıldığında (pinch close), ölçek azalır; uzaklaştırıldığında (pinch open), artar. Hareket süreklidir: sistem, parmaklar arasındaki mesafeyi gerçek zamanlı olarak izler ve ara olaylar üreterek uygulamanın ölçeği sorunsuz bir şekilde güncellemesine olanak tanır.
Hareketin mekaniği, iki dokunma noktası arasındaki mesafenin hesaplanmasına dayanır. iOS ve Android, Öklid mesafesini kullanır: sqrt((x2−x1)² + (y2−y1)²). Geçerli ölçek, mevcut mesafenin başlangıç mesafesine oranı olarak hesaplanır. Başlangıç mesafesi 100px ise ve 150px olursa — ölçek = 1.5 (%50 artış). iOS, önceki duruma göre ölçeği (delta) iletir, Android ise ScaleGestureDetector aracılığıyla birikimli ölçeği iletir.
Google Material Design Guidelines'a göre, Pinch-to-Zoom, görüntü, harita veya belge görüntüleyen uygulamalar için zorunludur. IT Sectr'de, görsel içerikle çalışan tüm projelerde — galerilerden görüntü düzenleyicilere kadar — bu hareketi UIPinchGestureRecognizer ve ScaleGestureDetector ile uyguluyoruz.
UIPinchGestureRecognizer, kıstırma hareketini tanımak için UIGestureRecognizer'ın sürekli bir alt sınıfıdır. Ayrık tanıyıcıların (UITapGestureRecognizer) aksine, bir olay dizisi üretir: .began (parmaklar ekrana dokunur), .changed (parmakların her hareketi), .ended (parmaklar kaldırılır).
Temel özellikler: scale (CGFloat) — hareketin başlangıcından bu yana parmaklar arasındaki mesafedeki göreceli değişim; velocity (CGFloat) — saniye başına ölçek değişim oranı. Başlangıç ölçeği = 1.0. Parmaklar uzaklaştırıldığında, ölçek artar (1.0 → 1.5 → 2.0); yaklaştırıldığında azalır (1.0 → 0.8 → 0.5). velocity özelliği atalet için kullanılır: .ended'den sonra, yavaşlamayla birlikte sürekli ölçeklendirmeyi canlandırabilirsiniz.
UIPinchGestureRecognizer, diğer tanıyıcılarla (haritada pan + pinch gibi) eşzamanlı tanıma için delegate özelliğini destekler. gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) yöntemi true döndürür ve her iki hareket paralel olarak çalışır. Apple, tek tek öğeler yerine kapsayıcı görünüme UIPinchGestureRecognizer eklenmesini önerir — bu, ölçeklendirme pivot noktasının hesaplanmasını basitleştirir.
ScaleGestureDetector, çoklu dokunmatik ölçeklendirmeyi tanımak için tasarlanmış Android Support Library (android.view) sınıfıdır. GestureDetector.onFling'in aksine, ScaleGestureDetector özellikle iki parmaklı yakınlaştırma için oluşturulmuştur ve otomatik olarak focusX, focusY (parmaklar arasındaki merkez noktası) ve scaleFactor'u (önceki olaydan ölçek deltası) hesaplar.
ScaleGestureDetector, üç yöntemi olan OnScaleGestureListener arayüzü aracılığıyla çalışır: onScaleBegin(detector) (.began'a benzer), onScale(detector) (.changed'a benzer, boolean döndürür — ölçek kabul edilirse true), onScaleEnd(detector) (.ended'e benzer). Özellikler: scaleFactor — önceki olaya göre ölçek çarpanı (yavaş uzaklaştırma için ~1.01), focusX/focusY — parmaklar arasındaki merkez noktasının koordinatları.
Bir View'a ölçeklendirme uygulamak için genellikle Matrix kullanılır: view.imageMatrix.postScale(scaleFactor, scaleFactor, focusX, focusY). Matrix, dönüşümlerin birikimini destekler — her postScale çağrısı, mevcut dönüşüme yeni bir dönüşüm ekler. Hatırlanması önemlidir: ScaleGestureDetector, minimum/maksimum ölçeği sınırlamaz — bu geliştiricinin sorumluluğudur. IT Sectr'de, özel ölçek sınırları (0.5–3.0) ve aşılması durumunda geri dönüş animasyonu ile ScaleGestureDetector kullanıyoruz.
Pinch-to-Zoom uygulanırken, minimum ve maksimum ölçeği sınırlamak son derece önemlidir — bu olmadan, kullanıcı sonsuza kadar yakınlaştırabilir veya görünmez olana kadar uzaklaştırabilir. Standart değerler: min = 0.5 (orijinal boyutun %50'si), max = 3.0 (%300). iOS'te, sınırlama kıskaç yoluyla yapılır: newScale = min(max(currentScale * gesture.scale, minScale), maxScale). Android'de — postScale'den sonra kontrol yoluyla.
Pivot noktası, ölçeklendirmenin gerçekleştirildiği parmaklar arasındaki merkez noktasıdır. iOS, iki dokunuşun ağırlık merkezini otomatik olarak hesaplar ve location(in:) aracılığıyla iletir — geliştiricinin pivotu manuel olarak hesaplaması gerekmez. Android, ScaleGestureDetector'da focusX/focusY sağlar, ancak Matrix aracılığıyla uygulanırken pivot, postScale(scaleFactor, scaleFactor, focusX, focusY) içinde üçüncü ve dördüncü argüman olarak iletilir. Pivot noktası olmadan, ölçeklendirme sol üst köşeye (0,0) göre gerçekleştirilir ve bu da yakınlaştırma sırasında içeriğin kaymasına neden olur.
IT Sectr'ın deneyimine göre, doğru pivot noktası hesaplaması, Pinch-to-Zoom uygulanırken en yaygın hata kaynağıdır. Vakaların %90'ında, GestureRecognizer ve ScaleGestureDetector'ın yerleşik koordinatlarını kullanmak yeterlidir — pivotu manuel olarak hesaplamaya gerek yoktur. Ölçeklendirme sırasında içerik kayıyorsa, focusX/focusY'yi matrix.postScale()'a ilettiğinizi kontrol edin.
Hareket sona erdikten sonra sorunsuz bir kullanıcı deneyimi için atalet ekleyebilirsiniz: iOS'te — UIView.animate ve yay parametreleriyle UIPinchGestureRecognizer'dan velocity kullanın; Android'de — OverScroller veya ValueAnimator. Atalet, min/max ölçek sınırlarını aşmamalıdır — sınır aşıldığında nihai değer, yay dönüş animasyonuyla kesilir.
0.5 ila 3.0 arasında ölçek sınırlamasıyla UIImageView'a UIPinchGestureRecognizer ekler. Birikimli ölçeği koruyan minimum bir uygulama gösterir.
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
}
}
}
Mantık: .began/.changed'te, ölçek kıskaçlı CGAffineTransform aracılığıyla uygulanır. .ended'te, ölçek 1.0'a sıfırlanır ve birikimli değer currentScale'de saklanır. Bu, tekrarlanan hareketler sırasında ölçek kaymasını önler — her yeni hareket mevcut durumdan başlar. Görüntüler için, CGAffineTransform yerine minimumZoomScale/maximumZoomScale ile UIScrollView önerilir.
Matrix ile ScaleGestureDetector aracılığıyla ImageView ölçeklendirmesini uygular. Doğru pivot noktası işlemesini ve ölçek sınırlamasını gösterir.
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
}
}
Özel ImageView, onTouchEvent'i geçersiz kılar ve olayları ScaleGestureDetector'a iletir. OnScaleListener.onScale(), her parmak hareketinde çağrılır. detector.scaleFactor deltadır (yavaş uzaklaştırma için 1.01). currentScale, kıskaçlama için coerceIn ile birikir. focusX/focusY — parmaklar arasındaki merkez noktası, doğru pivot ölçeklendirmesi için postScale'e iletilir.
UIScrollView, temsilcisi aracılığıyla yerleşik Pinch-to-Zoom desteğine sahiptir. Bu, görüntüler ve belgeler için tercih edilen yaklaşımdır — ScrollView, ölçeği, ataleti ve kaydırmayı otomatik olarak yönetir.
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 otomatik olarak bir UIPinchGestureRecognizer oluşturur, CALayer dönüşümü aracılığıyla ölçeklendirmeyi yönetir ve sınırlar aşıldığında sıçrama efekti ekler. Kod, min/maxZoomScale ayarlamaya ve viewForZooming döndürmeye indirgenir. IT Sectr'de, tüm tam ekran görüntüler için UIScrollView kullanıyoruz — bu, UIPinchGestureRecognizer aracılığıyla manuel uygulamaya kıyasla kod hacmini 5 kat azaltır.
Sıkça Sorulan Sorular
Dokunmatik ekranda, kıstırma aralarındaki mesafe izlemesiyle iki parmak kullanır. MacBook izleme dörtgeninde, yakınlaştırma aynı durumlarla (.began → .changed → .ended) UIPinchGestureRecognizer aracılığıyla uygulanır, ancak dokunma kaynağı kapasitif bir ekran değil, Force Touch'lı bir izleme dörtgenidir. API aynıdır, mekanik aynıdır.
iOS'te: UIPinchGestureRecognizer'dan ölçeği aldıktan sonra currentScale ile çarpın ve clamp(min: 0.5, max: 3.0) uygulayın. Android'de: matrix.postScale()'den önce coerceIn(minScale, maxScale) kullanın. UIScrollView'de — minimumZoomScale ve maximumZoomScale özellikleri aracılığıyla. Sınırlama olmadan, ölçek sonsuza veya sıfıra gidebilir.
Hayır, kıstırma en az iki parmak gerektirir. Tek parmaklı yakınlaştırma için (çift dokunma + yukarı/aşağı sürükleme), UIPanGestureRecognizer veya GestureDetector.onScroll aracılığıyla özel bir uygulama gerekir. Bu, farklı mantığa sahip farklı bir harekettir — genellikle kamera veya haritalarda yakınlaştırma için kullanılır.
Özet
Anahtar teslim bir mobil uygulama geliştireceğiz
IT Sectr, 2017'den beri girişimler ve işletmeler için iOS ve Android uygulamaları oluşturmaktadır. Size danışmanlık yapacak ve en iyi çözümü önereceğiz.
Ayrıca okuyun