Những điểm chính
UIGestureRecognizer là một lớp cơ sở trừu tượng trong UIKit, phân tách quá trình nhận dạng cử chỉ thành các đối tượng riêng biệt. Mỗi lớp con xử lý một loại cử chỉ: UITapGestureRecognizer xử lý một lần chạm với số lần chạm cụ thể, UISwipeGestureRecognizer xử lý một cú vuốt theo một hướng nhất định. Nhà phát triển thêm bộ nhận dạng vào UIView thông qua phương thức addGestureRecognizer(:), và UIKit tự động theo dõi các lần chạm, cập nhật trạng thái và gọi hành động khi nhận dạng.
Trước UIGestureRecognizer (iOS 3.2, 2010), các nhà phát triển ghi đè các phương thức UIResponder — touchesBegan, touchesMoved, touchesEnded — và phân tích thủ công quỹ đạo chạm. Điều này dẫn đến trùng lặp mã và lỗi khi xử lý các lần chạm đồng thời. Apple đã đóng gói logic này vào UIGestureRecognizer, thêm hỗ trợ cho đa chạm, hủy cử chỉ và hoạt động đồng thời của nhiều bộ nhận dạng trên một view.
Theo các phiên WWDC, Gesture Recognizer xử lý tới 11 lần chạm đồng thời trên iPad và 5 trên iPhone. Tại IT Sectr, chúng tôi sử dụng UIGestureRecognizer như phương thức tiêu chuẩn để xử lý đầu vào người dùng trong tất cả các dự án UIKit — điều này đã loại bỏ các lỗi liên quan đến theo dõi touchesBegan thủ công.
Mỗi UIGestureRecognizer trải qua 7 trạng thái khả thi, được định nghĩa trong enum UIGestureRecognizer.State. Các trạng thái này phản ánh vòng đời nhận dạng: từ phát hiện chạm đến hoàn thành hoặc hủy bỏ. Hiểu các trạng thái là rất quan trọng để triển khai bộ nhận dạng tùy chỉnh và gỡ lỗi xung đột.
| Trạng thái | Ý nghĩa | Khi nào xảy ra |
|---|---|---|
| .possible | Trạng thái ban đầu, cử chỉ chưa được nhận dạng | Ngay sau khi được thêm vào view |
| .began | Cử chỉ được nhận dạng và bắt đầu thực thi | Khi ngón tay di chuyển lần đầu cho pan/longPress |
| .changed | Tham số cử chỉ thay đổi (tọa độ, góc) | Mỗi khi ngón tay di chuyển |
| .ended | Người dùng nhấc ngón tay, cử chỉ hoàn tất | Tại touchesEnded |
| .cancelled | Cử chỉ bị hệ thống gián đoạn (cuộc gọi đến, thay đổi hướng) | Tại touchesCancelled |
| .failed | Cử chỉ không được nhận dạng theo điều kiện | Tại touchesCancelled mà không nhận dạng |
| .recognized | Từ đồng nghĩa của .ended; cử chỉ được nhận dạng thành công | Giống như .ended |
Cử chỉ rời rạc (tap, swipe) chuyển từ .possible trực tiếp sang .ended hoặc .failed. Cử chỉ liên tục (pan, pinch, rotation, longPress) trải qua .possible → .began → .changed (lặp lại) → .ended. Trong phương thức hành động, hãy kiểm tra gestureRecognizer.state — điều này cho phép phân biệt bắt đầu, thay đổi và kết thúc của một cử chỉ.
UIKit cung cấp 7 lớp con tích hợp của UIGestureRecognizer, bao phủ hầu hết các tình huống tương tác. Mỗi lớp con có các cài đặt cụ thể: numberOfTapsRequired cho tap, direction cho swipe, minimumPressDuration cho nhấn giữ.
Đối với cử chỉ tùy chỉnh (ví dụ: vẽ đường zic zac), hãy tạo một lớp con của UIGestureRecognizer bằng cách ghi đè touchesBegan, touchesMoved, touchesEnded và cập nhật trạng thái. Apple khuyến nghị sử dụng các lớp tích hợp khi có thể — chúng được tối ưu hóa và tương tác chính xác với nhau.
Khi nhiều UIGestureRecognizer được sử dụng trên một UIView (ví dụ: tap và nhấp đúp), sẽ xảy ra xung đột nhận dạng: khi nhấp đúp, tap đơn được kích hoạt trước. Apple cung cấp phương thức require(toFail:) để trì hoãn việc nhận dạng một cử chỉ cho đến khi cử chỉ khác thất bại.
Cơ chế hoạt động như sau: bằng cách gọi tapRecognizer.require(toFail: doubleTapRecognizer), bạn chỉ định rằng tapRecognizer sẽ chuyển sang trạng thái .recognized chỉ sau khi doubleTapRecognizer kết thúc ở .failed. Điều này thêm độ trễ ~0,3 giây trước khi thực hiện tap đơn — người dùng chạm hai lần và lần chạm đầu tiên bị bỏ qua. Một cách tiếp cận khác là ủy quyền UIGestureRecognizerDelegate với phương thức gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), cho phép nhận dạng đồng thời (ví dụ: pan + pinch cho bản đồ).
Theo WWDC 2020, khoảng 15% lỗi trong ứng dụng UIKit liên quan đến cấu hình xung đột cử chỉ không đúng. Tại IT Sectr, chúng tôi đã chuẩn hóa cách tiếp cận: mỗi màn hình có một sơ đồ cử chỉ với các ưu tiên require(toFail:) — điều này đã loại bỏ hoàn toàn lỗi kích hoạt kép khi chạm.
Thêm trình xử lý chạm đơn vào UIImageView. Khi chạm, độ mờ của hình ảnh thay đổi — một ví dụ đơn giản cho thấy việc gắn cử chỉ vào một view.
import UIKit
class ImageViewController: UIViewController {
@IBOutlet private var imageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(
target: self,
action: #selector(handleTap(_:))
)
tap.numberOfTapsRequired = 1
imageView.addGestureRecognizer(tap)
imageView.isUserInteractionEnabled = true
}
@objc private func handleTap(_: UITapGestureRecognizer) {
UIView.animate(withDuration: 0.2) {
self.imageView.alpha = self.imageView.alpha == 1.0 ? 0.5 : 1.0
}
}
}
Điểm chính: isUserInteractionEnabled trên UIImageView mặc định là false — không có cờ này, Gesture Recognizer sẽ không nhận được các lần chạm. Đối với UIView và UIButton, cờ này được bật theo mặc định. Phương thức hành động chấp nhận tham số UITapGestureRecognizer, qua đó có thể lấy location(in:) để xác định tọa độ chạm.
Triển khai vuốt sang trái để quay lại màn hình trước. Minh họa cấu hình hướng và gắn cử chỉ vào view gốc của bộ điều khiển.
import UIKit
class DetailViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let swipeLeft = UISwipeGestureRecognizer(
target: self,
action: #selector(handleSwipe(_:))
)
swipeLeft.direction = .left
view.addGestureRecognizer(swipeLeft)
}
@objc private func handleSwipe(_: UISwipeGestureRecognizer) {
navigationController?.popViewController(animated: true)
}
}
UISwipeGestureRecognizer là một cử chỉ rời rạc: nó chuyển sang .recognized ngay sau khi nhận dạng, không qua trạng thái .changed trung gian. Do đó, không cần kiểm tra trạng thái trong hành động — cử chỉ hoặc được nhận dạng (hành động được kích hoạt) hoặc không. Thuộc tính direction chấp nhận một trong bốn giá trị: .left, .right, .up, .down. Để hỗ trợ nhiều hướng, hãy tạo các bộ nhận dạng riêng cho mỗi hướng.
Cho thấy cách cấu hình chạm đơn và nhấp đúp trên cùng một view mà không xung đột. Bộ nhận dạng nhấp đúp có ưu tiên — chạm đơn chỉ được kích hoạt nếu nhấp đúp không được nhận dạng.
import UIKit
class TapViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let singleTap = UITapGestureRecognizer(
target: self,
action: #selector(handleSingleTap)
)
singleTap.numberOfTapsRequired = 1
let doubleTap = UITapGestureRecognizer(
target: self,
action: #selector(handleDoubleTap)
)
doubleTap.numberOfTapsRequired = 2
singleTap.require(toFail: doubleTap)
view.addGestureRecognizer(singleTap)
view.addGestureRecognizer(doubleTap)
}
@objc private func handleSingleTap() {
print("Single tap — after 0.3s delay")
}
@objc private func handleDoubleTap() {
print("Double tap — instant")
}
}
Không có require(toFail:), khi nhấp đúp, handleSingleTap được kích hoạt trước, sau đó handleDoubleTap — điều này phá vỡ trải nghiệm người dùng. Với require(toFail:), chạm đơn đợi ~0,3 giây để đảm bảo không có lần chạm thứ hai. Tại IT Sectr, mẫu này được sử dụng trong trình chỉnh sửa ảnh và thư viện, nơi nhấp đúp phóng to và chạm đơn chọn một phần tử.
Câu hỏi thường gặp
Có, UIView hỗ trợ nhiều phiên bản UIGestureRecognizer cùng lúc. Để giải quyết xung đột, sử dụng phương thức require(toFail:) để thiết lập thứ tự nhận dạng. Đối với hoạt động cử chỉ song song (ví dụ: pan + pinch trên bản đồ), hãy triển khai phương thức ủy quyền gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) trả về true.
UIGestureRecognizer là một sự trừu tượng cấp cao tự động nhận dạng mẫu chạm và quản lý trạng thái. touchesBegan là một phương thức cấp thấp của UIResponder yêu cầu theo dõi tọa độ thủ công, tính thời gian và hủy chạm. Gesture Recognizer đơn giản hơn, đáng tin cậy hơn và được ưu tiên cho các cử chỉ tiêu chuẩn; touchesBegan chỉ phù hợp cho đồ họa tùy chỉnh.
SwiftUI gốc sử dụng bộ điều chỉnh: onTapGesture, onLongPressGesture, DragGesture, MagnificationGesture, RotationGesture. Đây là các phiên bản khai báo tương tự của UIGestureRecognizer được tích hợp trong hệ phân cấp SwiftUI. Nếu cần, có thể bọc một bộ nhận dạng UIKit qua UIViewRepresentable, nhưng Apple khuyến nghị sử dụng cử chỉ SwiftUI gốc.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm