Gesture Recognizer — nó là gì, xử lý chạm trong iOS

Tác giả: IT Sectr Đã đăng: 2026-02-27 Thời gian đọc: 8 phút
Gesture Recognizer là một sự trừu tượng trong UIKit, chuyển đổi chuỗi các lần chạm của người dùng thành một cử chỉ được nhận dạng: chạm, vuốt, nhấn giữ, chụm hoặc xoay. Thay vì theo dõi tọa độ thủ công trong các phương thức touchesBegan/touchesMoved, nhà phát triển thêm một đối tượng UIGestureRecognizer có sẵn vào UIView và triển khai một phương thức hành động. Theo Tài liệu dành cho Nhà phát triển Apple, UIGestureRecognizer hỗ trợ 7 lớp con tích hợp bao phủ 95% cử chỉ điển hình trong ứng dụng di động.

Những điểm chính

  • UIGestureRecognizer là một lớp trừu tượng của UIKit để nhận dạng các mẫu chạm: chạm, vuốt, chụm, xoay và nhấn giữ.
  • 7 lớp con tích hợp: UITapGestureRecognizer, UISwipeGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer, UILongPressGestureRecognizer, UIPanGestureRecognizer, UIScreenEdgePanGestureRecognizer.
  • Mỗi bộ nhận dạng có một trạng thái (possible, began, changed, ended, cancelled, failed) được cập nhật khi ngón tay di chuyển.
  • Để giải quyết xung đột giữa các cử chỉ, sử dụng phương thức require(toFail:) để thiết lập ưu tiên nhận dạng.
  • Gesture Recognizer hoạt động với UIKit và UIViewRepresentable trong SwiftUI, nhưng SwiftUI gốc sử dụng bộ điều chỉnh cử chỉ.

Gesture Recognizer là gì?

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.

Trạng thái của UIGestureRecognizer

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ỉ.

Các loại cử chỉ trong iOS

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ữ.

  • UITapGestureRecognizer — nhận dạng các lần chạm đơn và nhiều lần. Có thể cấu hình số lần chạm (numberOfTapsRequired) và số ngón tay (numberOfTouchesRequired). Được sử dụng cho nút, liên kết, chọn phần tử.
  • UISwipeGestureRecognizer — nhận dạng một cú vuốt theo một trong bốn hướng (right, left, up, down). Thuộc tính direction đặt hướng; numberOfTouchesRequired đặt số ngón tay.
  • UIPanGestureRecognizer — cử chỉ liên tục để kéo. Phương thức translation(in:) trả về độ dịch chuyển từ điểm bắt đầu. Được sử dụng cho kéo-thả, băng chuyền, kéo để làm mới.
  • UIPinchGestureRecognizer — thu phóng bằng hai ngón tay. Thuộc tính scale phản ánh hệ số tỷ lệ hiện tại. Được trình bày chi tiết trong bài viết Pinch-to-Zoom.
  • UIRotationGestureRecognizer — xoay bằng hai ngón tay. Thuộc tính rotation lưu góc theo radian. Được sử dụng để xoay hình ảnh, bản đồ, canvas.
  • UILongPressGestureRecognizer — nhấn giữ. Tham số: minimumPressDuration (giây), allowableMovement (pixel). Được sử dụng cho menu ngữ cảnh, kéo phần tử.
  • UIScreenEdgePanGestureRecognizer — vuốt từ mép màn hình. Thuộc tính edges chỉ định mép nào cần theo dõi. Được sử dụng cho cử chỉ điều hướng, bảng thông báo.

Đố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.

Giải quyết xung đột giữa các cử chỉ

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.

Ví dụ mã Swift

Ví dụ 1: UITapGestureRecognizer — Chạm vào hình ảnh

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.

swift
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.

Ví dụ 2: UISwipeGestureRecognizer — Vuốt để điều hướng

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.

swift
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.

Ví dụ 3: Kết hợp cử chỉ với require(toFail:)

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.

swift
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ó thể sử dụng nhiều Gesture Recognizer trên một view không?

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 khác với touchesBegan như thế nào?

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.

Gesture Recognizer có hoạt động trong SwiftUI không?

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

  • UIGestureRecognizer là một sự trừu tượng của UIKit để phân tách logic nhận dạng cử chỉ thành các đối tượng riêng biệt với quản lý trạng thái tự động.
  • 7 lớp con tích hợp bao phủ 95% cử chỉ điển hình: chạm, vuốt, kéo, chụm, xoay, nhấn giữ và vuốt mép.
  • Mỗi bộ nhận dạng trải qua 7 trạng thái — từ .possible đến .ended/.failed, cho phép theo dõi chính xác cử chỉ rời rạc và liên tục.
  • Phương thức require(toFail:) giải quyết xung đột giữa các cử chỉ trên một view bằng cách thiết lập ưu tiên nhận dạng.
  • SwiftUI sử dụng bộ điều chỉnh cử chỉ khai báo, nhưng bộ nhận dạng UIKit có sẵn qua UIViewRepresentable.
  • Gesture Recognizer là bắt buộc cho các dự án UIKit; touchesBegan chỉ phù hợp cho vẽ tùy chỉnh và đồ họa cấp thấp.
  • Sử dụng require(toFail:) cho cặp chạm/nhấp đúp và gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:) cho các cử chỉ song song.

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.

Thảo luận dự án

Đọc thêm