Accessibility Trait คือคุณสมบัติขององค์ประกอบ iOS ที่กำหนดบทบาทและพฤติกรรมสำหรับ VoiceOver เทรตจะบอกโปรแกรมอ่านหน้าจอว่าองค์ประกอบควรถูกอ่านอย่างไรและมีท่าทางใดบ้างที่ใช้ได้: ไม่ว่าจะเป็นปุ่ม หัวเรื่อง ลิงก์ หรือช่องค้นหา ตามข้อมูลจาก Apple UIAccessibilityTraits, 2024 ระบบรองรับค่าคงที่กว่า 15 รายการที่สามารถรวมกันได้โดยใช้บิตมาสก์ การเลือกเทรตที่ถูกต้องช่วยประหยัดเวลาในการนำทางได้ถึง 50% สำหรับผู้ใช้ VoiceOver
ประเด็นสำคัญ
Accessibility Trait คือแฟล็กที่ตั้งบนองค์ประกอบ UIView เพื่อระบุบทบาทเชิงความหมายให้ VoiceOver เทรตเป็นหนึ่งในสามองค์ประกอบของสามกลุ่มการช่วยเหลือของ Apple: Label (ชื่อ), Hint (คำอธิบาย), Trait (บทบาท) iOS ใช้บิตมาสก์ UIAccessibilityTraits (UInt64) ซึ่งแต่ละบิตสอดคล้องกับบทบาทเฉพาะ VoiceOver อ่านบทบาทหลังจาก Label และ Hint: “ปุ่มส่ง จะเปิดแบบฟอร์ม” — “ปุ่ม” ถูกเพิ่มขึ้นด้วยเทรต UIAccessibilityTraitButton
โดยค่าเริ่มต้น UIButton จะได้รับ UIAccessibilityTraitButton, UILabel จะได้รับ UIAccessibilityTraitStaticText, UIImageView จะได้รับ UIAccessibilityTraitImage เมื่อใช้ตัวควบคุมแบบกำหนดเอง นักพัฒนาจะต้องตั้งค่าเทรตด้วยตนเอง Apple Human Interface Guidelines, 2024 เรียกสิ่งนี้ว่า “หนึ่งในขั้นตอนที่สำคัญที่สุดในการรับประกันการช่วยเหลือ”
หากไม่มีเทรตที่ถูกต้อง ผู้ใช้จะไม่รู้ว่าควรใช้ท่าทางใด: แตะครั้งเดียว (การเปิดใช้งานปุ่ม), แตะสองครั้ง (ซูม) หรือท่าทางปัด (สวิตช์) เทรตจะกำหนดว่าท่าทาง VoiceOver ใดที่จะเปิดใช้งานบนองค์ประกอบ
UIAccessibilityTraits คือ typealias UInt64 แต่ละเทรตเป็นค่าคงที่ที่มีการตั้งค่าบิตเดียวเท่านั้น ตัวอย่างเช่น UIAccessibilityTraitButton = 0x0000000000000001, UIAccessibilityTraitLink = 0x0000000000000002, UIAccessibilityTraitHeader = 0x0000000000000008 การรวมกันทำได้โดยใช้ OR ระดับบิต: 0x0001 | 0x0008 = 0x0009 VoiceOver วิเคราะห์มาสก์และกำหนดพฤติกรรม
iOS มีค่าคงที่เทรตมากกว่า 15 รายการ มาดูประเภทหลักที่ใช้ใน 90% ของสถานการณ์:
| เทรต | ค่าคงที่ | พฤติกรรม VoiceOver |
|---|---|---|
| Button | UIAccessibilityTraitButton | เปิดใช้งานด้วยการแตะสองครั้ง |
| Header | UIAccessibilityTraitHeader | นำทางด่วนตามหัวเรื่อง |
| Link | UIAccessibilityTraitLink | เปิดใช้งานเป็นลิงก์ |
| StaticText | UIAccessibilityTraitStaticText | อ่านอย่างเดียว ไม่มีการเปิดใช้งาน |
| SearchField | UIAccessibilityTraitSearchField | ช่องค้นหาที่มีพฤติกรรมพิเศษ |
| Image | UIAccessibilityTraitImage | รูปภาพ ไม่มีท่าทางเปิดใช้งาน |
| Selected | UIAccessibilityTraitSelected | สถานะ “เลือกแล้ว” |
| PlaysSound | UIAccessibilityTraitPlaysSound | เล่นเสียงเมื่อเปิดใช้งาน |
| KeyboardKey | UIAccessibilityTraitKeyboardKey | ปุ่มแป้นพิมพ์ |
| TabBar | UIAccessibilityTraitTabBar | องค์ประกอบแถบแท็บ |
ค่าคงที่พร้อมใช้งานใน UIKit ตั้งแต่ iOS 3.0 iOS 14+ เพิ่มการรองรับ UIAccessibilityTraits ใน SwiftUI ผ่านตัวปรับแต่ง .accessibilityAddTraits()
UIAccessibilityTraitAdjustable — สำหรับค่าที่ปรับได้ (แถบเลื่อน, ตัวเลือก, แถบเลื่อนระดับเสียง) VoiceOver อนุญาตให้ปัดขึ้น/ลงเพื่อเปลี่ยนค่าด้วยขั้นตอนที่กำหนดผ่าน accessibilityIncrement และ accessibilityDecrement UIAccessibilityTraitUpdatesFrequently — สำหรับองค์ประกอบที่มีค่าเปลี่ยนแปลงบ่อย (ตัวจับเวลา, ตัวบ่งชี้ความคืบหน้า) VoiceOver จะไม่อ่านค่าในทุกการเปลี่ยนแปลง แต่จะหยุดชั่วคราว UIAccessibilityTraitAllowsDirectInteraction — สำหรับองค์ประกอบที่ผู้ใช้สามารถโต้ตอบได้โดยตรง (แป้นพิมพ์, การวาด) โดยข้ามท่าทาง VoiceOver
องค์ประกอบเดียวสามารถมีหลายเทรตพร้อมกันได้ — การรวมถูกตั้งค่าโดยใช้ OR ระดับบิต (|) ตัวอย่าง: ปุ่มที่ถูกเลือกอยู่ในขณะนี้ — Button | Selected VoiceOver จะประกาศ: “เลือกแล้ว กรองตามราคา ปุ่ม”
การตั้งค่าเทรตในโค้ด:
filterButton.accessibilityTraits.insert(.button)
filterButton.accessibilityTraits.insert(.selected)
// หรือผ่านมาสก์:
filterButton.accessibilityTraits = [.button, .selected]
สำหรับ UIView ที่กำหนดเองซึ่งไม่ได้ตั้งค่าเทรตโดยค่าเริ่มต้น:
class CustomToggle: UIControl {
override var accessibilityTraits: UIAccessibilityTraits {
get {
if isOn {
return [.button, .selected]
} else {
return .button
}
}
set {}
}
}
กฎการรวม: ไม่เกิน 3-4 เทรต ต่อองค์ประกอบ เทรตที่มากเกินไป (เช่น Button + Link + Header) ทำให้การประกาศ VoiceOver ยาวและสับสนเกินไป ตามข้อมูลของ Apple “คุณสมบัติเพิ่มเติมแต่ละอย่างจะเพิ่มภาระทางความคิดของผู้ใช้”
ใน SwiftUI เทรตถูกตั้งค่าโดยใช้ตัวปรับแต่ง .accessibilityAddTraits() และ .accessibilityRemoveTraits() ตัวอย่าง: Text(“หัวเรื่อง”).font(.largeTitle).accessibilityAddTraits(.isHeader) ตัวปรับแต่ง .isHeader จะเพิ่ม UIAccessibilityTraitHeader รายการเทรต SwiftUI: .isButton, .isHeader, .isLink, .isSelected, .isImage, .isSearchField, .isKeyboardKey, .isStaticText, .isSummaryElement, .isToggle, .playsSound, .startsMediaSession, .updatesFrequently, .allowsDirectInteraction, .causesPageTurn, .isModal, .tabBar
StaticText แทน Button — ตัวควบคุมที่กำหนดเองซึ่งดูเหมือนปุ่มจะได้รับเทรต StaticText โดยค่าเริ่มต้น VoiceOver จะไม่มีท่าทางเปิดใช้งาน ดังนั้นผู้ใช้จึงไม่สามารถ “กด” องค์ประกอบได้ วิธีแก้ไข: ตั้งค่า .button อย่างชัดเจน
รูปภาพไม่มีเทรต — UIImageView ที่เปิดใช้งานการช่วยเหลือจะได้รับเทรต Image แม้ว่าจริงๆ แล้วมันคือปุ่มสำหรับขยายรูปภาพ กำหนด .button และ Label “ขยายรูปภาพ” ตามข้อมูลจาก WWDC 2023, “Deliver an Exceptional Accessibility Experience” 40% ของการถดถอยด้านการช่วยเหลือในเวอร์ชันแอปใหม่เกิดจากความไม่ตรงกันของเทรต
Header ในทุกองค์ประกอบ — เทรต Header มีไว้สำหรับหัวเรื่องโครงสร้างของหน้าจอ หากทำให้ทุก UILabel เป็นหัวเรื่อง โรเตอร์ VoiceOver ในโหมด “หัวเรื่อง” จะไร้ประโยชน์ — มันจะหยุดทุกคำ
สาเหตุทั่วไปของการสูญเสียเทรตคือการปรับโครงสร้าง: นักพัฒนาเปลี่ยน UIButton เป็น UIControl สำหรับการแสดงผลแบบกำหนดเอง UIButton จะได้รับเทรต Button โดยอัตโนมัติ แต่ UIControl ไม่ได้รับ หลังจากการปรับโครงสร้าง คุณต้องตั้งค่า accessibilityTraits = .button อย่างชัดเจน เพิ่มการตรวจสอบในการตรวจสอบโค้ด: “หากคุณเปลี่ยน UIButton เป็น UIControl — ให้ตรวจสอบเทรต”
สำหรับองค์ประกอบที่มีสถานะเปลี่ยนแปลง (เช่น ปุ่มถูกใจ) เทรตควรเปลี่ยนแบบไดนามิก ในสถานะ “ไม่ถูกใจ” — Button ในสถานะ “ถูกใจ” — Button + Selected + Image (หากมีไอคอน) VoiceOver เปลี่ยนแปลงการประกาศ: “ถูกใจ ปุ่ม” เทียบกับ “เลือกแล้ว ถูกใจ ปุ่ม” ใช้ accessibilityValue เพื่อสื่อสารสถานะหากเทรต Selected ไม่เพียงพอ เกี่ยวข้องกับปุ่มสมัครสมาชิก รายการโปรด ตัวกรอง และสวิตช์
ใน Android ไม่มีสิ่งที่เทียบเท่าโดยตรงกับเทรต แทนที่จะใช้บิตมาสก์ จะใช้สิ่งต่อไปนี้:
สำหรับ View ที่กำหนดเองใน Android คุณต้องแทนที่ onInitializeAccessibilityNodeInfo:
class CustomButton @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null
) : View(context, attrs) {
override fun onInitializeAccessibilityNodeInfo(
info: AccessibilityNodeInfo
) {
super.onInitializeAccessibilityNodeInfo(info)
info.className = "android.widget.Button"
info.isClickable = true
}
}
นักพัฒนา Flutter ควรใช้พารามิเตอร์ semanticsRole ในวิดเจ็ต Semantics: button, header, image, link, textField และอื่นๆ นอกจากนี้ยังมี semanticsLabel และ semanticsHint — สิ่งที่เทียบเท่าเต็มรูปแบบของสามกลุ่ม iOS Label + Hint + Trait
สำหรับเวอร์ชันเว็บของแอปพลิเคชันมือถือ (PWA, WebView) จะใช้แอตทริบิวต์ role จาก WAI-ARIA: role="button", role="heading", role="link" นี่คือสิ่งที่เทียบเท่าโดยตรงของ accessibilityTraits ในแอปพลิเคชันไฮบริด ให้ตรวจสอบว่า WebView ส่งผ่านบทบาท ARIA ไปยังชั้นการช่วยเหลือดั้งเดิม ในการทำเช่นนี้ ให้ใช้โปรโตคอล UIAccessibilityContainerDataTable ใน iOS หรือ setAccessibilityDelegate ใน Android WebView ที่เปิดใช้ JavaScript อาจไม่ส่งผ่านบทบาท ARIA อย่างถูกต้อง — ทดสอบแยกต่างหาก
ใน Android คุณสามารถเพิ่มการกระทำที่กำหนดเองใน AccessibilityNodeInfo: AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK และ ACTION_LONG_CLICK นี่คือสิ่งที่เทียบเท่ากับเทรต Button ที่มีท่าทางเพิ่มเติม สำหรับแถบเลื่อน ให้ใช้ ACTION_SET_PROGRESS — สิ่งที่เทียบเท่ากับ Adjustable สำหรับ Spinner และ DatePicker — ACTION_SET_SELECTION, ACTION_SET_DATE และ ACTION_SET_TIME
Xcode Accessibility Inspector เป็นเครื่องมือหลักสำหรับ iOS: เลือกองค์ประกอบและดูฟิลด์ Traits จะแสดงรายการเทรตที่ตั้งไว้ โรเตอร์ VoiceOver ในโหมด “องค์ประกอบ” ช่วยให้สามารถนำทางผ่านตัวควบคุมทั้งหมดบนหน้าจอ
การทดสอบอัตโนมัติใน Swift เพื่อตรวจสอบเทรต:
func testSubmitButtonTrait() {
let app = XCUIApplication()
app.launch()
let submitButton = app.buttons["ส่ง"]
XCTAssertTrue(submitButton.isEnabled)
// XCUIElement ไม่ให้การเข้าถึงเทรตโดยตรง
// การตรวจสอบผ่านการเปิดใช้งานท่าทาง
submitButton.tap()
XCTAssertTrue(app.staticTexts["ส่งแบบฟอร์มแล้ว"].exists)
}
การตรวจสอบด้วยตนเอง ผ่าน VoiceOver: เปิด VoiceOver ปัดไปยังองค์ประกอบ แตะสองครั้ง — องค์ประกอบควรเปิดใช้งานหากเป็น Button หากองค์ประกอบไม่ตอบสนองต่อการแตะสองครั้ง แสดงว่าเทรตไม่ถูกต้อง ใช้ท่าทาง Rotor เพื่อสลับระหว่างโหมด (“หัวเรื่อง”, “ลิงก์”, “ปุ่ม”) — แต่ละโหมดจะแสดงเฉพาะองค์ประกอบที่มีเทรตที่เกี่ยวข้อง
ก่อน iOS 14 การทดสอบหน่วยไม่สามารถเข้าถึง accessibilityTraits ได้โดยตรง ตั้งแต่ iOS 14 เป็นต้นไป คุณสมบัตินี้พร้อมใช้งาน: XCTAssertEqual(customButton.accessibilityTraits, .button) ใช้สิ่งนี้ในการทดสอบหน่วยเพื่อตรวจสอบตัวควบคุมที่กำหนดเอง แนะนำให้ทดสอบ UIView ที่กำหนดเองใหม่ทุกครั้งเพื่อความถูกต้องของเทรต โดยเฉพาะหลังจากการปรับโครงสร้างหรือเปลี่ยนแปลงคลาสแม่
คำถามที่พบบ่อย
สูงสุด 3-4 เทรต ต่อองค์ประกอบ จำนวนที่มากกว่าทำให้การประกาศ VoiceOver ซ้ำซ้อน ใช้การรวม: Button + Selected, Header + StaticText
UIAccessibilityTraitButton iOS จะตั้งค่าให้โดยอัตโนมัติสำหรับอินสแตนซ์ UIButton ทั้งหมด หากคุณสืบทอดจาก UIView และจำลองปุ่ม จะต้องตั้งค่าเทรตด้วยตนเอง
ใช่ UIAccessibilityTraitAdjustable — สำหรับองค์ประกอบที่มีค่าที่ปรับได้ (แถบเลื่อน, ตัวเลือก, ตัวนับ) VoiceOver อนุญาตให้ปัดขึ้น/ลงเพื่อเปลี่ยนค่าและอ่านสถานะปัจจุบัน
ใช้ตัวปรับแต่ง .accessibilityAddTraits(): Text(“หัวเรื่อง”).font(.title).accessibilityAddTraits(.isHeader) เมธอดนี้ทำงานบน iOS 14+
VoiceOver จะกำหนดเทรต None องค์ประกอบจะไม่มีบทบาท — โปรแกรมอ่านหน้าจอจะอ่านเฉพาะ Label โดยไม่ระบุประเภท ผู้ใช้จะไม่รู้ว่ามีท่าทางเปิดใช้งานหรือไม่
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม