Responder Chain là một cơ chế iOS truyền tuần tự các sự kiện chạm, nhấn phím và cử chỉ qua hệ thống phân cấp các đối tượng UIResponder cho đến khi một trong số chúng xử lý sự kiện. Chuỗi bắt đầu với đối tượng phát hiện sự kiện và di chuyển lên trên hệ thống phân cấp: từ view đến superview của nó, sau đó đến view controller, window và cuối cùng đến UIApplication. Theo Apple Developer Documentation (2026), mẫu này cho phép phân chia trách nhiệm xử lý sự kiện giữa các thành phần giao diện, cung cấp tính linh hoạt mà không bị ràng buộc cứng nhắc với một trình xử lý cụ thể.
Những điểm chính
Responder Chain là một chuỗi các đối tượng UIResponder mà qua đó iOS truyền các sự kiện đầu vào như chạm, nhấn phím và dữ liệu gia tốc kế. Mỗi đối tượng trong chuỗi này có khả năng xử lý sự kiện hoặc chuyển nó đến đối tượng phản hồi tiếp theo thông qua thuộc tính next.
Cơ chế dựa trên hệ thống phân cấp view: khi một cú chạm xảy ra, iOS trước tiên xác định view nào đã được chạm (thông qua hit-testing) và tạo một chuỗi bắt đầu từ view đó lên đến UIApplication. UIApplication là mắt xích cuối cùng của chuỗi — nếu một sự kiện đến được nó và không được xử lý, nó sẽ bị loại bỏ.
Theo Apple, mẫu này rất quan trọng để đóng gói logic xử lý sự kiện. Nhà phát triển có thể ghi đè hành vi của một view cụ thể mà không ảnh hưởng đến các phần tử khác trong hệ thống phân cấp. Ví dụ, UITextField trở thành người phản hồi đầu tiên khi nhận tiêu điểm và nhận các sự kiện bàn phím mà không yêu cầu thay đổi trong UIViewController cha.
Xử lý sự kiện thông qua Responder Chain diễn ra theo hai giai đoạn: đầu tiên, iOS xác định view nào đã nhận được sự kiện (hit-testing), sau đó chạy chuỗi phản hồi để xử lý nó. Nếu một đối tượng không triển khai phương thức tương ứng, sự kiện sẽ được chuyển tiếp.
Chuỗi được hình thành động dựa trên người phản hồi đầu tiên hiện tại và hệ thống phân cấp view. Thứ tự tiêu chuẩn là: người phản hồi đầu tiên → view của nó → superview → UIViewController → root view → UIWindow → UIApplication. Nếu bất kỳ đối tượng nào trong số này triển khai, ví dụ, touchesBegan, sự kiện sẽ được xử lý ở cấp độ đó và không được chuyển tiếp.
import UIKit
class CustomView: UIView {
override func touchesBegan(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
// Chạm được xử lý ở cấp view này
print("CustomView đã xử lý chạm")
// Chuyển tiếp sự kiện dọc theo chuỗi
super.touchesBegan(touches, with: event)
}
}
Một tính năng chính là việc gọi super.touchesBegan không bắt buộc. Nếu không gọi nó, sự kiện sẽ chỉ được xử lý ở cấp độ hiện tại và sẽ không tiếp tục dọc theo Responder Chain. Điều này cho nhà phát triển toàn quyền kiểm soát đối tượng nào tham gia vào quá trình xử lý.
Khi một đối tượng nhận được sự kiện nhưng không xử lý nó (không ghi đè phương thức), iOS tự động chuyển sự kiện đến đối tượng phản hồi tiếp theo thông qua thuộc tính next. Thuộc tính này tạo thành một danh sách liên kết đơn, được gọi là Responder Chain.
UIViewController nằm giữa view của nó và UIWindow: nếu view không xử lý sự kiện, bộ điều khiển có cơ hội làm điều đó. Điều này đặc biệt hữu ích cho logic chung — ví dụ, xử lý một cử chỉ nên hoạt động trên toàn bộ scene, bất kể view nào dưới ngón tay người dùng.
Hit-Testing là quá trình iOS xác định view nào nằm dưới điểm chạm. Phương thức hitTest:withEvent: duyệt hệ thống phân cấp view từ UIWindow trở xuống, kiểm tra view con nào chứa điểm chạm và không bị ẩn.
Thuật toán hoạt động đệ quy: cho mỗi cấp độ, iOS kiểm tra các view theo thứ tự ngược của việc thêm (trên cùng trước). Nếu một view không bị ẩn, không trong suốt và điểm nằm trong bounds của nó, hitTest được chạy đệ quy cho tất cả các subview của nó. View sâu nhất đáp ứng tất cả các điều kiện trở thành hit-test view — đối tượng đầu tiên trong Responder Chain.
override func hitTest(
_ point: CGPoint,
with event: UIEvent?
) -> UIView? {
if isUserInteractionEnabled &&
isHidden == false &&
alpha > 0.01 &&
point(inside: point, with: event) {
return super.hitTest(point, with: event)
}
return nil
}
Các nhà phát triển có thể ghi đè hitTest để thay đổi hành vi mặc định. Ví dụ, để mở rộng vùng chạm cho một nút nhỏ hoặc chuyển hướng sự kiện đến một view khác không nằm dưới ngón tay. Đây là một công cụ mạnh mẽ để tạo các phần tử tương tác tùy chỉnh.
UIResponder là lớp cơ sở cho tất cả các đối tượng có thể xử lý sự kiện trong iOS. UIView, UIViewController, UIApplication và UIWindow kế thừa từ UIResponder. Lớp này cung cấp một tập hợp các phương thức có thể được ghi đè để xử lý các loại sự kiện khác nhau.
Các nhóm phương thức chính bao gồm touchesBegan, touchesMoved, touchesEnded, touchesCancelled cho chạm; pressesBegan, pressesEnded cho nút vật lý; và motionBegan, motionEnded cho sự kiện gia tốc kế. Mỗi phương thức nhận một tập hợp các đối tượng UITouch hoặc UIPress và một tham chiếu đến UIEvent chứa siêu dữ liệu bổ sung về sự kiện.
| Phương thức UIResponder | Mục đích |
|---|---|
| touchesBegan | Được gọi khi bắt đầu chạm |
| touchesMoved | Được gọi khi ngón tay di chuyển |
| touchesEnded | Được gọi khi nhấc ngón tay |
| touchesCancelled | Được gọi khi bị gián đoạn (cuộc gọi, vuốt trung tâm điều khiển) |
| pressesBegan | Được gọi khi nhấn nút vật lý |
Điều quan trọng là iOS chỉ gọi các phương thức này cho người phản hồi đầu tiên và các đối tượng tiếp theo trong chuỗi. Nếu không có đối tượng nào ghi đè phương thức, sự kiện không tạo ra lỗi — nó chỉ đơn giản bị bỏ qua. Để gỡ lỗi xử lý sự kiện, hãy sử dụng Symbolic Breakpoint trên UIResponder touchEvent.
Các thành phần UIKit tiêu chuẩn sử dụng tích cực Responder Chain cho hoạt động của chúng. UITextField trở thành người phản hồi đầu tiên khi nhận tiêu điểm, tự động mở bàn phím. UIButton xử lý chạm thông qua cơ chế UIControl, cũng dựa trên chuỗi phản hồi.
UITableView và UICollectionView sử dụng chuỗi phản hồi để xử lý lựa chọn ô và cử chỉ cuộn. Nếu người dùng chạm vào một ô, sự kiện đầu tiên đến ô đó, sau đó đến UITableView, và chỉ sau đó đến UIViewController. UIGestureRecognizer có ưu tiên cao hơn touchesBegan — nếu một bộ nhận dạng cử chỉ được thêm vào view, nó sẽ nhận sự kiện trước.
Theo Apple, việc sử dụng đúng Responder Chain rất quan trọng cho khả năng tiếp cận của ứng dụng. VoiceOver và các công nghệ hỗ trợ khác sử dụng chuỗi phản hồi để điều hướng giữa các phần tử giao diện. Nếu chuỗi bị đứt, người dùng khuyết tật sẽ không thể tương tác với ứng dụng.
UIMenuController để hiển thị menu ngữ cảnh cũng sử dụng Responder Chain. Khi người dùng gọi menu, hệ thống tìm kiếm người phản hồi đầu tiên triển khai phương thức canPerformAction và các phương thức hành động tương ứng. Menu chỉ được hiển thị cho các hành động mà người phản hồi hiện tại hỗ trợ.
Điều này cho phép, ví dụ, hiển thị các lệnh Cắt, Sao chép, Dán chỉ khi UITextField đang được tiêu điểm và ẩn chúng khi làm việc với UILabel. Nhà phát triển có thể thêm các hành động tùy chỉnh vào menu ngữ cảnh bằng cách triển khai chúng trong lớp con của UIResponder và trả về true từ canPerformAction.
Tạo một đối tượng phản hồi tùy chỉnh cho nhà phát triển toàn quyền kiểm soát việc xử lý sự kiện. Để làm điều này, bạn cần tạo một lớp con của UIResponder (hoặc UIView/UIViewController) và ghi đè các phương thức xử lý sự kiện cần thiết.
Các đối tượng phản hồi tùy chỉnh thường được sử dụng để xử lý các cử chỉ cụ thể không được bao phủ bởi UIGestureRecognizer tiêu chuẩn. Ví dụ, nhận dạng vẽ hình dạng, kết hợp đa chạm phức tạp hoặc các mẫu nhập liệu độc quyền. Một bộ phản hồi tùy chỉnh có thể tổng hợp các sự kiện từ nhiều ngón tay và đưa ra quyết định dựa trên sự kết hợp của chúng.
class DrawingResponder: UIResponder {
private var activeTouches: [UITouch: CGPoint] = [:]
override func touchesBegan(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
for touch in touches {
activeTouches[touch] = touch.location(in: self)
}
}
override func touchesMoved(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
for touch in touches {
let currentPoint = touch.location(in: self)
activeTouches[touch] = currentPoint
drawLine(from: activeTouches[touch]!, to: currentPoint)
}
}
}
Khi tạo một bộ phản hồi tùy chỉnh, điều quan trọng là phải cấu hình đúng chuỗi next. Nếu đối tượng của bạn không thuộc hệ thống phân cấp UIKit tiêu chuẩn, bạn phải chỉ định rõ ràng đối tượng nào sẽ là next responder của nó. Điều này đảm bảo rằng các sự kiện chưa được xử lý tiếp tục di chuyển dọc theo Responder Chain.
Câu hỏi thường gặp
Responder Chain là một chuỗi phân cấp các đối tượng UIResponder mà qua đó iOS truyền tuần tự các sự kiện chạm, nhấn và cử chỉ. Nếu một đối tượng không xử lý sự kiện, nó sẽ được chuyển đến đối tượng phản hồi tiếp theo trong chuỗi cho đến UIApplication.
Bạn có thể thay đổi thứ tự bằng cách ghi đè thuộc tính next của đối tượng UIResponder của bạn. Bằng cách trả về một đối tượng khác thay vì đối tượng mặc định, bạn chuyển hướng các sự kiện chưa được xử lý đến nó. Điều này hữu ích cho các hệ thống phân cấp không tiêu chuẩn, ví dụ, khi một vùng chứa tùy chỉnh quản lý nhiều bộ điều khiển con.
Hit-Testing xác định view nào nằm dưới điểm chạm (người nhận ban đầu), trong khi Responder Chain xác định cách sự kiện được truyền giữa các đối tượng sau hit-test. Hit-test tìm đối tượng đầu tiên, chuỗi phản hồi cung cấp định tuyến thêm nếu đối tượng đó không xử lý sự kiện.
Để ngắt chuỗi, chỉ cần xử lý sự kiện trong UIResponder của bạn và không gọi super. Ví dụ, bằng cách ghi đè touchesBegan và không gọi super.touchesBegan, bạn ngăn sự kiện được truyền tiếp. Sự kiện sẽ được xử lý ở cấp độ hiện tại và sẽ không đến được các mắt xích tiếp theo của chuỗi.
Những lý do phổ biến nhất: isUserInteractionEnabled được đặt thành false, view bị ẩn (isHidden = true), alpha nhỏ hơn 0,01, hoặc view nằm ngoài bounds của vùng chứa cha. Cũng kiểm tra xem có UIGestureRecognizer nào trên view hoặc superview của nó chặn các sự kiện trước touchesBegan không.
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