মূল পয়েন্ট
Pinch-to-Zoom (চিমটি) একটি মাল্টি-টাচ জেসচার যা প্রদর্শিত কন্টেন্টের স্কেল পরিবর্তন করতে দুটি আঙুল ব্যবহার করে। আঙুল কাছাকাছি আনলে (pinch close), স্কেল কমে; ছড়িয়ে দিলে (pinch open), বাড়ে। জেসচারটি ক্রমাগত: সিস্টেম রিয়েল টাইমে আঙুলের মধ্যে দূরত্ব ট্র্যাক করে এবং মধ্যস্থ ইভেন্ট তৈরি করে, যা অ্যাপ্লিকেশনকে মসৃণভাবে স্কেল আপডেট করতে দেয়।
জেসচারের মেকানিক্স দুটি স্পর্শ বিন্দুর মধ্যে দূরত্ব গণনার উপর ভিত্তি করে। iOS এবং Android ইউক্লিডীয় দূরত্ব ব্যবহার করে: sqrt((x2−x1)² + (y2−y1)²)। বর্তমান স্কেল বর্তমান দূরত্ব এবং প্রাথমিক দূরত্বের অনুপাত হিসাবে গণনা করা হয়। যদি প্রাথমিক দূরত্ব 100px হয় এবং 150px হয় — স্কেল = 1.5 (50% বৃদ্ধি)। iOS পূর্ববর্তী অবস্থার (ডেল্টা) সাপেক্ষে স্কেল পাস করে, Android ScaleGestureDetector এর মাধ্যমে সঞ্চিত স্কেল পাস করে।
Google Material Design Guidelines অনুসারে, Pinch-to-Zoom অ্যাপ্লিকেশনের জন্য বাধ্যতামূলক যা ছবি, মানচিত্র বা ডকুমেন্ট প্রদর্শন করে। IT Sectr এ, আমরা ভিজ্যুয়াল কন্টেন্ট নিয়ে কাজ করে এমন সব প্রজেক্টে UIPinchGestureRecognizer এবং ScaleGestureDetector দিয়ে এই জেসচার বাস্তবায়ন করি — গ্যালারি থেকে ইমেজ এডিটর পর্যন্ত।
UIPinchGestureRecognizer পিঞ্চ জেসচার শনাক্ত করার জন্য UIGestureRecognizer এর একটি ক্রমাগত উপশ্রেণী। বিচ্ছিন্ন রিকগনাইজার (UITapGestureRecognizer) এর বিপরীতে, এটি ইভেন্টের একটি ক্রম তৈরি করে: .began (আঙুল স্ক্রিন স্পর্শ করে), .changed (আঙুলের প্রতিটি নড়াচড়া), .ended (আঙুল তুলে নেওয়া হয়)।
মূল প্রপার্টি: scale (CGFloat) — জেসচার শুরুর পর থেকে আঙুলের মধ্যে দূরত্বের আপেক্ষিক পরিবর্তন; velocity (CGFloat) — প্রতি সেকেন্ডে স্কেল পরিবর্তনের হার। প্রাথমিক স্কেল = 1.0। আঙুল ছড়ালে, স্কেল বাড়ে (1.0 → 1.5 → 2.0); কাছাকাছি আনলে, কমে (1.0 → 0.8 → 0.5)। velocity প্রপার্টি জড়তার জন্য ব্যবহৃত হয়: .ended এর পরে, আপনি মন্দার সাথে ক্রমাগত স্কেলিং অ্যানিমেট করতে পারেন।
UIPinchGestureRecognizer অন্যান্য রিকগনাইজারের (যেমন, মানচিত্রে pan + pinch) সাথে একসাথে শনাক্তকরণের জন্য delegate প্রপার্টি সমর্থন করে। gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) মেথড true রিটার্ন করে, এবং উভয় জেসচার সমান্তরালে কাজ করে। Apple পৃথক উপাদানের পরিবর্তে কন্টেইনার ভিউতে UIPinchGestureRecognizer যোগ করার পরামর্শ দেয় — এটি স্কেলিং পিভট পয়েন্ট গণনা সহজ করে।
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 এ, আমরা কাস্টম স্কেল সীমানা (0.5–3.0) এবং সীমা অতিক্রম করলে রিটার্ন অ্যানিমেশন সহ ScaleGestureDetector ব্যবহার করি।
Pinch-to-Zoom বাস্তবায়ন করার সময়, ন্যূনতম এবং সর্বোচ্চ স্কেল সীমিত করা অত্যন্ত গুরুত্বপূর্ণ — এটি ছাড়া, ব্যবহারকারী অসীম পর্যন্ত জুম ইন বা অদৃশ্যতা পর্যন্ত জুম আউট করতে পারে। স্ট্যান্ডার্ড মান: min = 0.5 (মূল আকারের 50%), max = 3.0 (300%)। iOS এ, সীমা ক্ল্যাম্পের মাধ্যমে করা হয়: newScale = min(max(currentScale * gesture.scale, minScale), maxScale)। Android এ — postScale এর পরে চেকের মাধ্যমে।
পিভট পয়েন্ট হল আঙুলের মধ্যবর্তী কেন্দ্র বিন্দু, যার সাপেক্ষে স্কেলিং করা হয়। iOS স্বয়ংক্রিয়ভাবে দুটি স্পর্শের কেন্দ্রক গণনা করে এবং location(in:) এর মাধ্যমে পাস করে — ডেভেলপারকে ম্যানুয়ালি পিভট গণনা করতে হয় না। Android ScaleGestureDetector এ focusX/focusY প্রদান করে, কিন্তু Matrix এর মাধ্যমে প্রয়োগ করার সময়, পিভট postScale(scaleFactor, scaleFactor, focusX, focusY) এ তৃতীয় এবং চতুর্থ আর্গুমেন্ট হিসাবে পাস করা হয়। পিভট পয়েন্ট ছাড়া, স্কেলিং উপরের-বাম কোণ (0,0) এর সাপেক্ষে করা হবে, যা জুমের সময় কন্টেন্ট স্থানচ্যুত করে।
IT Sectr এর অভিজ্ঞতা অনুসারে, সঠিক পিভট পয়েন্ট গণনা Pinch-to-Zoom বাস্তবায়নে বাগের সবচেয়ে সাধারণ উৎস। 90% ক্ষেত্রে, GestureRecognizer এবং ScaleGestureDetector এর অন্তর্নির্মিত স্থানাঙ্ক ব্যবহার করা যথেষ্ট — ম্যানুয়ালি পিভট গণনা করার প্রয়োজন নেই। যদি স্কেলিং এর সময় কন্টেন্ট সরে যায়, তবে চেক করুন আপনি focusX/focusY matrix.postScale() এ পাস করছেন কিনা।
জেসচার শেষ হওয়ার পর মসৃণ UX এর জন্য, আপনি জড়তা যোগ করতে পারেন: iOS এ — UIView.animate এবং স্প্রিং প্যারামিটার সহ UIPinchGestureRecognizer থেকে velocity ব্যবহার করুন; Android এ — OverScroller বা ValueAnimator। জড়তা min/max স্কেল সীমানা অতিক্রম করা উচিত নয় — সীমা অতিক্রম করলে স্প্রিং-রিটার্ন অ্যানিমেশন সহ চূড়ান্ত মান কেটে দেওয়া হয়।
0.5 থেকে 3.0 পর্যন্ত স্কেল সীমা সহ UIImageView এ UIPinchGestureRecognizer যোগ করে। সঞ্চিত স্কেল সংরক্ষণ সহ ন্যূনতম বাস্তবায়ন দেখায়।
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 এর মাধ্যমে স্কেল প্রয়োগ করা হয়। .ended এ, স্কেল 1.0 এ রিসেট হয় এবং সঞ্চিত মান currentScale এ সংরক্ষিত হয়। এটি বারবার জেসচারের সময় স্কেল ড্রিফট প্রতিরোধ করে — প্রতিটি নতুন জেসচার বর্তমান অবস্থা থেকে শুরু হয়। ছবির জন্য, CGAffineTransform এর পরিবর্তে minimumZoomScale/maximumZoomScale সহ UIScrollView সুপারিশ করা হয়।
Matrix সহ ScaleGestureDetector এর মাধ্যমে ImageView স্কেলিং বাস্তবায়ন করে। সঠিক পিভট পয়েন্ট হ্যান্ডলিং এবং স্কেল সীমা দেখায়।
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 দিয়ে সঞ্চিত হয়। focusX/focusY — আঙুলের মধ্যে কেন্দ্র বিন্দু, সঠিক পিভট স্কেলিং এর জন্য postScale এ পাস করা হয়।
UIScrollView এর ডেলিগেটের মাধ্যমে অন্তর্নির্মিত Pinch-to-Zoom সমর্থন রয়েছে। এটি ছবি এবং ডকুমেন্টের জন্য পছন্দের পদ্ধতি — ScrollView স্বয়ংক্রিয়ভাবে স্কেল, জড়তা এবং স্ক্রোলিং পরিচালনা করে।
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 রূপান্তরের মাধ্যমে স্কেলিং পরিচালনা করে এবং সীমানা অতিক্রম করলে বাউন্স ইফেক্ট যোগ করে। কোড min/maxZoomScale সেট করা এবং viewForZooming রিটার্ন করা পর্যন্ত সীমাবদ্ধ। IT Sectr এ, আমরা সব ফুল-স্ক্রিন ছবির জন্য UIScrollView ব্যবহার করি — এটি UIPinchGestureRecognizer এর মাধ্যমে ম্যানুয়াল বাস্তবায়নের তুলনায় কোড ভলিউম 5 গুণ কমায়।
সচরাচর জিজ্ঞাস্য
টাচ স্ক্রিনে, পিঞ্চ তাদের মধ্যে দূরত্ব ট্র্যাকিং সহ দুটি আঙুল ব্যবহার করে। MacBook ট্র্যাকপ্যাডে, জুম UIPinchGestureRecognizer এর মাধ্যমে একই অবস্থা (.began → .changed → .ended) সহ বাস্তবায়িত হয়, কিন্তু স্পর্শের উৎস Force Touch ট্র্যাকপ্যাড, ক্যাপাসিটিভ স্ক্রিন নয়। API একই, মেকানিক্স অভিন্ন।
iOS এ: UIPinchGestureRecognizer থেকে স্কেল পাওয়ার পর, currentScale দিয়ে গুণ করুন এবং clamp(min: 0.5, max: 3.0) প্রয়োগ করুন। Android এ: matrix.postScale() এর আগে coerceIn(minScale, maxScale) ব্যবহার করুন। UIScrollView এ — minimumZoomScale এবং maximumZoomScale প্রপার্টির মাধ্যমে। সীমা ছাড়া, স্কেল অসীম বা শূন্যে যেতে পারে।
না, পিঞ্চের জন্য কমপক্ষে দুটি আঙুল প্রয়োজন। এক-আঙুলের জুমের জন্য (ডাবল-ট্যাপ + উপরে/নিচে টানা), UIPanGestureRecognizer বা GestureDetector.onScroll এর মাধ্যমে কাস্টম বাস্তবায়ন প্রয়োজন। এটি ভিন্ন যুক্তি সহ একটি ভিন্ন জেসচার — সাধারণত ক্যামেরা বা মানচিত্রে জুমের জন্য ব্যবহৃত হয়।
সারাংশ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন