Responder Chain เป็นกลไกของ iOS ที่ส่งต่อเหตุการณ์การสัมผัส การกดแป้นพิมพ์ และท่าทางตามลำดับผ่านลำดับชั้นของออบเจ็กต์ UIResponder จนกว่าหนึ่งในนั้นจะจัดการกับเหตุการณ์ ห่วงโซ่เริ่มต้นด้วยออบเจ็กต์ที่ตรวจพบเหตุการณ์และเคลื่อนที่ขึ้นไปตามลำดับชั้น: จาก view ไปยัง superview จากนั้นไปยัง view controller, window และสุดท้ายไปยัง UIApplication ตาม Apple Developer Documentation (2026) รูปแบบนี้ช่วยแยกความรับผิดชอบในการจัดการเหตุการณ์ระหว่างส่วนประกอบอินเทอร์เฟซ ให้ความยืดหยุ่นโดยไม่ต้องผูกติดกับตัวจัดการเฉพาะ
ประเด็นสำคัญ
Responder Chain คือลำดับของออบเจ็กต์ UIResponder ที่ iOS ใช้ส่งต่อเหตุการณ์อินพุต เช่น การสัมผัส การกดแป้นพิมพ์ และข้อมูล accelerometer ออบเจ็กต์แต่ละตัวในห่วงโซ่นี้มีความสามารถในการจัดการเหตุการณ์หรือส่งต่อไปยังออบเจ็กต์ผู้ตอบสนองถัดไปผ่านคุณสมบัติ next
กลไกนี้ขึ้นอยู่กับลำดับชั้นของ view: เมื่อมีการสัมผัสเกิดขึ้น iOS จะกำหนดก่อนว่า view ใดถูกสัมผัส (ผ่าน hit-testing) และสร้างห่วงโซ่เริ่มจาก view นั้นขึ้นไปจนถึง UIApplication UIApplication คือจุดเชื่อมต่อสุดท้ายของห่วงโซ่ — หากเหตุการณ์มาถึงและไม่ได้รับการจัดการ มันจะถูกละทิ้ง
ตามข้อมูลของ Apple รูปแบบนี้มีความสำคัญอย่างยิ่งสำหรับ การห่อหุ้มตรรกะการจัดการเหตุการณ์ นักพัฒนาสามารถโอเวอร์ไรด์พฤติกรรมของ view เฉพาะได้โดยไม่ส่งผลกระทบต่อองค์ประกอบอื่น ๆ ในลำดับชั้น ตัวอย่างเช่น UITextField จะกลายเป็นผู้ตอบสนองคนแรกเมื่อได้รับโฟกัสและรับเหตุการณ์แป้นพิมพ์โดยไม่ต้องเปลี่ยนแปลงใน UIViewController หลัก
การจัดการเหตุการณ์ ผ่าน Responder Chain เกิดขึ้นในสองขั้นตอน: ก่อนอื่น iOS กำหนดว่า view ใดได้รับเหตุการณ์ (hit-testing) จากนั้นจึงเรียกใช้ห่วงโซ่ผู้ตอบสนองเพื่อจัดการ หากออบเจ็กต์ไม่ได้ใช้เมธอดที่เกี่ยวข้อง เหตุการณ์จะถูกส่งต่อ
ห่วงโซ่ถูกสร้างขึ้นแบบไดนามิกตามผู้ตอบสนองคนแรกปัจจุบันและลำดับชั้นของ view ลำดับมาตรฐานคือ: ผู้ตอบสนองคนแรก → view ของมัน → superview → UIViewController → root view → UIWindow → UIApplication หากออบเจ็กต์ใด ๆ เหล่านี้ใช้ เช่น touchesBegan เหตุการณ์จะถูกจัดการในระดับนั้นและจะไม่ถูกส่งต่อ
import UIKit
class CustomView: UIView {
override func touchesBegan(
_ touches: Set<UITouch>,
with event: UIEvent?
) {
// จัดการการสัมผัสที่ระดับ view นี้
print("CustomView จัดการการสัมผัส")
// ส่งต่อเหตุการณ์ไปตามห่วงโซ่
super.touchesBegan(touches, with: event)
}
}
คุณสมบัติที่สำคัญคือการเรียก super.touchesBegan ไม่จำเป็น หากไม่เรียก เหตุการณ์จะถูกจัดการเฉพาะในระดับปัจจุบันเท่านั้นและจะไม่ดำเนินต่อไปตาม Responder Chain สิ่งนี้ทำให้นักพัฒนาควบคุมได้อย่างสมบูรณ์ว่าออบเจ็กต์ใดมีส่วนร่วมในการจัดการ
เมื่อออบเจ็กต์ได้รับเหตุการณ์แต่ไม่ได้จัดการ (ไม่ได้โอเวอร์ไรด์เมธอด) iOS จะส่งต่อเหตุการณ์ไปยังออบเจ็กต์ผู้ตอบสนองถัดไปโดยอัตโนมัติผ่านคุณสมบัติ next คุณสมบัตินี้สร้างรายการเชื่อมโยงเดี่ยว ซึ่งเรียกว่า Responder Chain
UIViewController อยู่ระหว่าง view ของมันกับ UIWindow: หาก view ไม่จัดการเหตุการณ์ ตัวควบคุมจะมีโอกาสจัดการ สิ่งนี้มีประโยชน์อย่างยิ่งสำหรับ ตรรกะร่วม — ตัวอย่างเช่น การจัดการท่าทางที่ควรทำงานทั่วทั้งฉาก โดยไม่ขึ้นกับว่า view ใดอยู่ใต้นิ้วของผู้ใช้
Hit-Testing คือกระบวนการที่ iOS ใช้กำหนดว่า view ใดอยู่ใต้จุดสัมผัส เมธอด hitTest:withEvent: จะสำรวจลำดับชั้นของ view จาก UIWindow ลงไปด้านล่าง ตรวจสอบว่า view ย่อยใดมีจุดสัมผัสและไม่ถูกซ่อน
อัลกอริทึมทำงานแบบเรียกซ้ำ: สำหรับแต่ละระดับ iOS จะตรวจสอบ view ในลำดับย้อนกลับของการเพิ่ม (บนสุดก่อน) หาก view ไม่ถูกซ่อน ไม่โปร่งใส และจุดอยู่ในขอบเขตของมัน hitTest จะถูกเรียกใช้แบบเรียกซ้ำสำหรับ subview ทั้งหมด view ที่ลึกที่สุด ที่ตรงตามเงื่อนไขทั้งหมดจะกลายเป็น hit-test view — ออบเจ็กต์แรกใน 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
}
นักพัฒนาสามารถ โอเวอร์ไรด์ hitTest เพื่อเปลี่ยนพฤติกรรมเริ่มต้น ตัวอย่างเช่น เพื่อขยายพื้นที่สัมผัสสำหรับปุ่มเล็ก ๆ หรือเปลี่ยนเส้นทางเหตุการณ์ไปยัง view อื่นที่ไม่ได้อยู่ใต้นิ้วโดยตรง นี่เป็นเครื่องมือที่ทรงพลังสำหรับการสร้างองค์ประกอบแบบโต้ตอบที่กำหนดเอง
UIResponder คือคลาสพื้นฐานสำหรับออบเจ็กต์ทั้งหมดที่สามารถจัดการเหตุการณ์ใน iOS UIView, UIViewController, UIApplication และ UIWindow สืบทอดจาก UIResponder คลาสนี้มีชุดเมธอดที่สามารถโอเวอร์ไรด์เพื่อจัดการเหตุการณ์ประเภทต่าง ๆ
กลุ่มเมธอดหลักประกอบด้วย touchesBegan, touchesMoved, touchesEnded, touchesCancelled สำหรับการสัมผัส; pressesBegan, pressesEnded สำหรับปุ่มกายภาพ; และ motionBegan, motionEnded สำหรับเหตุการณ์ accelerometer แต่ละเมธอด จะได้รับชุดออบเจ็กต์ UITouch หรือ UIPress และการอ้างอิงถึง UIEvent ที่มีข้อมูลเมตาเพิ่มเติมเกี่ยวกับเหตุการณ์
| เมธอด UIResponder | วัตถุประสงค์ |
|---|---|
| touchesBegan | เรียกเมื่อเริ่มสัมผัส |
| touchesMoved | เรียกเมื่อนิ้วเคลื่อนที่ |
| touchesEnded | เรียกเมื่อยกนิ้ว |
| touchesCancelled | เรียกเมื่อถูกรบกวน (สายเรียกเข้า, ปัดศูนย์ควบคุม) |
| pressesBegan | เรียกเมื่อกดปุ่มกายภาพ |
สิ่งสำคัญคือต้องเข้าใจว่า iOS เรียกเมธอดเหล่านี้เฉพาะสำหรับ ผู้ตอบสนองคนแรก และออบเจ็กต์ถัดไปในห่วงโซ่ หากไม่มีออบเจ็กต์ใดโอเวอร์ไรด์เมธอด เหตุการณ์จะไม่สร้างข้อผิดพลาด — มันจะถูกเพิกเฉย สำหรับการดีบักการจัดการเหตุการณ์ ให้ใช้ Symbolic Breakpoint บน UIResponder touchEvent
ส่วนประกอบ UIKit มาตรฐานใช้ Responder Chain อย่างแข็งขันสำหรับการทำงานของมัน UITextField จะกลายเป็นผู้ตอบสนองคนแรกเมื่อได้รับโฟกัส ซึ่งจะเปิดแป้นพิมพ์โดยอัตโนมัติ UIButton จัดการการสัมผัสผ่านกลไก UIControl ซึ่งขึ้นอยู่กับห่วงโซ่ผู้ตอบสนองเช่นกัน
UITableView และ UICollectionView ใช้ห่วงโซ่ผู้ตอบสนองสำหรับจัดการการเลือกเซลล์และท่าทางการเลื่อน หากผู้ใช้สัมผัสเซลล์ เหตุการณ์จะไปถึงเซลล์ก่อน จากนั้นจึง UITableView และสุดท้าย UIViewController UIGestureRecognizer มีลำดับความสำคัญสูงกว่า touchesBegan — หากเพิ่มตัวรู้จำท่าทางลงใน view มันจะได้รับเหตุการณ์ก่อน
ตามข้อมูลของ Apple การใช้ Responder Chain อย่างถูกต้องมีความสำคัญอย่างยิ่งสำหรับ การเข้าถึง ของแอป VoiceOver และเทคโนโลยีช่วยเหลืออื่น ๆ ใช้ห่วงโซ่ผู้ตอบสนองเพื่อนำทางระหว่างองค์ประกอบอินเทอร์เฟซ หากห่วงโซ่ขาด ผู้ใช้ที่มีความพิการจะไม่สามารถโต้ตอบกับแอปพลิเคชันได้
UIMenuController สำหรับแสดงเมนูบริบทก็ใช้ Responder Chain เช่นกัน เมื่อผู้ใช้เรียกเมนู ระบบจะมองหาผู้ตอบสนองคนแรกที่ใช้เมธอด canPerformAction และเมธอดการดำเนินการที่เกี่ยวข้อง เมนูจะแสดงเฉพาะสำหรับการดำเนินการที่ผู้ตอบสนองปัจจุบันรองรับ
สิ่งนี้ช่วยให้ ตัวอย่างเช่น แสดงคำสั่งตัด คัดลอก วาง เฉพาะเมื่อ UITextField อยู่ในโฟกัส และซ่อนเมื่อทำงานกับ UILabel นักพัฒนาสามารถเพิ่มการดำเนินการที่กำหนดเองลงในเมนูบริบทโดยการimplement ในคลาสย่อยของ UIResponder และส่งคืน true จาก canPerformAction
การสร้างออบเจ็กต์ผู้ตอบสนอง ที่กำหนดเองทำให้นักพัฒนาควบคุมการจัดการเหตุการณ์ได้อย่างสมบูรณ์ ในการทำเช่นนี้ คุณต้องสร้างคลาสย่อยของ UIResponder (หรือ UIView/UIViewController) และโอเวอร์ไรด์เมธอดการจัดการเหตุการณ์ที่จำเป็น
ออบเจ็กต์ผู้ตอบสนองที่กำหนดเองมักใช้สำหรับจัดการ ท่าทางเฉพาะ ที่ไม่ครอบคลุมโดย UIGestureRecognizer มาตรฐาน ตัวอย่างเช่น การรู้จำการวาดรูปทรง การรวมกันแบบมัลติทัชที่ซับซ้อน หรือรูปแบบอินพุตที่เป็นกรรมสิทธิ์ ผู้ตอบสนองที่กำหนดเองสามารถรวบรวมเหตุการณ์จากหลายนิ้วและตัดสินใจตามการรวมกันของมัน
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)
}
}
}
เมื่อสร้างผู้ตอบสนองที่กำหนดเอง สิ่งสำคัญคือต้องกำหนดค่า ห่วงโซ่ next อย่างถูกต้อง หากออบเจ็กต์ของคุณไม่ได้เป็นส่วนหนึ่งของลำดับชั้น UIKit มาตรฐาน คุณต้องระบุอย่างชัดเจนว่าออบเจ็กต์ใดจะเป็น next responder ของมัน สิ่งนี้ช่วยให้แน่ใจว่าเหตุการณ์ที่ไม่ได้รับการจัดการ将继续เคลื่อนที่ไปตาม Responder Chain
คำถามที่พบบ่อย
Responder Chain คือห่วงโซ่ตามลำดับชั้นของออบเจ็กต์ UIResponder ที่ iOS ใช้ส่งต่อเหตุการณ์การสัมผัส การกด และท่าทางตามลำดับ หากออบเจ็กต์ไม่จัดการเหตุการณ์ มันจะถูกส่งต่อไปยังออบเจ็กต์ผู้ตอบสนองถัดไปในห่วงโซ่จนถึง UIApplication
คุณสามารถเปลี่ยนลำดับได้โดยการโอเวอร์ไรด์คุณสมบัติ next ของออบเจ็กต์ UIResponder ของคุณ โดยการส่งคืนออบเจ็กต์อื่นแทนค่าเริ่มต้น คุณจะเปลี่ยนเส้นทางเหตุการณ์ที่ไม่ได้รับการจัดการไปยังมัน สิ่งนี้มีประโยชน์สำหรับลำดับชั้นที่ไม่เป็นมาตรฐาน เช่น เมื่อคอนเทนเนอร์ที่กำหนดเองจัดการตัวควบคุมย่อยหลายตัว
Hit-Testing กำหนดว่า view ใดอยู่ใต้จุดสัมผัส (ผู้รับเริ่มต้น) ในขณะที่ Responder Chain กำหนดว่าเหตุการณ์ถูกส่งต่อระหว่างออบเจ็กต์หลังจาก hit-test อย่างไร Hit-test ค้นหาออบเจ็กต์แรก ห่วงโซ่ผู้ตอบสนองให้การกำหนดเส้นทางเพิ่มเติมหากออบเจ็กต์นั้นไม่จัดการเหตุการณ์
เพื่อขัดจังหวะห่วงโซ่ เพียงจัดการเหตุการณ์ใน UIResponder ของคุณและ ไม่เรียก super ตัวอย่างเช่น โดยการโอเวอร์ไรด์ touchesBegan และไม่เรียก super.touchesBegan คุณป้องกันไม่ให้เหตุการณ์ถูกส่งต่อ เหตุการณ์จะถูกจัดการในระดับปัจจุบันและจะไม่ถึงจุดเชื่อมต่อถัดไปของห่วงโซ่
สาเหตุที่พบบ่อยที่สุด: isUserInteractionEnabled ถูกตั้งค่าเป็น false, view ถูกซ่อน (isHidden = true), alpha น้อยกว่า 0.01 หรือ view อยู่นอกขอบเขตของคอนเทนเนอร์หลัก นอกจากนี้ให้ตรวจสอบว่าไม่มี UIGestureRecognizer บน view หรือ superview ของมันที่สกัดกั้นเหตุการณ์ก่อน touchesBegan
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม