Accessibility Label คือชื่อของอารม์ส่วนติดต่อผู้ใช้ที่ VoiceOver (iOS) หรือ TalkBack (Android) พูดเมื่อได้รับโฟกัส ใน iOS คุณสมบัตินี้เรียกว่า accessibilityLabel ใน Android — contentDescription สำหรับอารม์ที่ไม่มีข้อความ ตาม Apple Developer Documentation, 2024 ลาเบลเป็นรากฐานของการเข้าถึงได้: หากไม่มีมัน ผู้ใช้จะไม่สามารถระบุอารม์ได้ ลาเบลต้องเป็นเอกเฉพาะในหน้าจอและสะท้อนได้เถิงแท้แท้ของอารม์ในภาษาที่ชัดเจน
ข้อสำคัญ
Accessibility Label เป็นคุณสมบัติข้อความที่กำหนดชื่อของอารม์สำหรับเทคโนโลยีช่วยเหลือ เมื่อผู้ใช้ปัดบนหน้าจอโดยเปิด VoiceOver ไว้ โปรแกรมอ่านหน้าจอจะอ่าน Label ของอารม์ที่ได้รับโฟกัส หากไม่มีลาเบล ผู้ใช้จะได้ยินเฉพาะชนิดของอารม์: “ปุ่ม”, “รูปภาพ” — โดยไม่บ่งบอกวัตถุประสงค์
ตาม Google I/O 2024, “Accessibility Testing” 35% ของการละเมิดการเข้าถึงได้ที่ร้ายแรงในแอปพลิเคชันในร้านค้าเกี่ยวข้องกับการขาดหรือไม่ถูกต้องของลาเบล Accessibility Scanner บน Android ตรวจจับการขาดลาเบลว่าเป็นข้อผิดพลาดร้ายแรงที่สุด
ข้อจำกัดพื้นฐาน: ลาเบลต้องไม่มีชนิดของอารม์ VoiceOver และ TalkBack จะเพิ่มบทบาท (ปุ่ม, ส่วนหัว, ลิ้งค์) ในการประกาศโดยอัตโนมัติ หากลาเบลมี “ปุ่มส่ง” ผู้ใช้จะได้ยิน: “ปุ่มส่ง, ปุ่ม” — การซ้ำกัน
WCAG 4.1.2 (ระดับ A) กำหนดให้แต่ละอารม์ส่วนติดต่อผู้ใช้มี ชื่อ, บทบาท และค่าที่สามารถกำหนดโดยโปรแกรมได้ Accessibility Label ให้ชื่อ หากขาดลาเบล เกณฑ์จะถูกถือว่าละเมิด และแอปพลิเคชันจะไม่ผ่านการรับรองรับขั้นพื้นฐาน
ใน iOS accessibilityLabel ถูกสืบทอดโดย UIView ทั้งหมดจากโปรโตคอล UIAccessibility หากอารม์มีข้อความ (UIButton ที่มีชื่อเรียก, UILabel ที่มีข้อความ) Label จะถูกตั้งเป็นข้อความนั้นโดยอัตโนมัติ สำหรับ UIImageView, ตัวควบคุมแบบกำหนดเอง และคอนเทนเนอร์ จำเป็นต้องตั้ง Label ด้วยตนเอง
ตัวอย่างของเซลตารางแบบกำหนดเอง:
class CustomTableViewCell: UITableViewCell {
let titleLabel = UILabel()
let priceLabel = UILabel()
override func awakeFromNib() {
super.awakeFromNib()
self.isAccessibilityElement = true
self.accessibilityLabel =
"\(titleLabel.text ?? "") - \(priceLabel.text ?? "")"
}
}
สำหรับ UIView แบบกำหนดเอง คุณสามารถเกินค่าตัวดึง accessibilityLabel:
class RatingView: UIView {
var rating: Int = 5
override var accessibilityLabel: String? {
get { return "การให้คะแนน: \(rating) จาก 5" }
set {}
}
}
Apple HIG, 2024 แนะนำ: หากอารม์ประกอบด้วยอารม์ย่อยหลายรายการ (เช่น บัตรสินค้าที่มีชื่อและราคา) ให้รวมพวกมันเป็นอารม์การเข้าถึงได้เดียวที่มี Label ผสม ตั้ง isAccessibilityElement = true บนองค์ประกอบหลัก และ false บนองค์ประกอบย่อย
หาก UILabel ใช้ NSAttributedString accessibilityLabel โดยค่าเริ่มต้นจะเท่ากับ .string (ข้อความธรรมดา) หากคุณจำเป็นต้องส่งค่าที่ แตกต่างทางความหมาย (เช่น ไอคอนสัญลักษณ์ถูกอ่านว่า “ดาว” แทนที่จะเป็นอักษร ★) ให้ตั้ง accessibilityLabel อย่างชัดเจน VoiceOver ไม่อ่านอักขระ Unicode อย่างมีความหมาย
ใน Android contentDescription ทำหน้าที่เป็น Label สำหรับ ImageView, ImageButton และ View แบบกำหนดเอง สำหรับ TextView และ Button ที่มีข้อความในตัว ไม่จำเป็นต้องตั้ง contentDescription — TalkBack จะอ่านข้อความโดยอัตโนมัติ
การตั้งค่าโดยโปรแกรมผ่าน Kotlin:
binding.iconStar.contentDescription = "สินค้าที่ชื่นชอบ"
// สำหรับ View แบบกำหนดเองที่มีหลายองค์ประกอบ
binding.customCard.setContentDescription(
"\(title) ในราคา \(price)")
ใน XML สำหรับองค์ประกอบตกแต่ง:
<ImageView
android:contentDescription="@null"
android:src="@drawable/divider"
android:importantForAccessibility="no" />
คุณสมบัติ importantForAccessibility = “no” จะไม่รวมอารม์นั้นในต้นไม้การเข้าถึงได้ ใน iOS สิ่งที่เทียบเทียงคือ isAccessibilityElement = false
ใน Jetpack Compose Label ถูกตั้งผ่านตัวแก้ไข semantics:
Image(
painter = painterResource(R.drawable.ic_search),
contentDescription = "ค้นหาสินค้า",
modifier = Modifier.semantics {
contentDescription = "ค้นหาสินค้า"
}
)
ใน Compose contentDescription เป็นพารามิเตอร์ บังคับ สำหรับ Image — หากไม่มีโค้ดจะไม่สามารถคอมไพล์ได้ (คำเตือน) ซึ่งช่วยบังคับปรับปรุงการเข้าถึงได้ผ่านการออกแบบ API
Accessibility Label ตอบคำถาม “อารม์นี้คืออะไร?” Hint (accessibilityHint ใน iOS, ข้อความเพิ่มเติมใน contentDescription ใน Android) — “จะเกิดอะไรขึ้นเมื่อแตะต้อง?” VoiceOver จะประกาศสิ่งเหล่านี้เรียงตาม: ก่อนเป็น Label จากนั้น Hint
ตัวอย่างของ ปุ่มลบ:
ตาม Deque University, 2024 การแยก Label และ Hint อย่างเหมาะสมช่วยเพิ่มอัตราการทำงานสำเร็จสำหรับผู้ใช้ VoiceOver ได้ 28% ผู้ใช้ที่มีความบกพร่องทางด้านสมองจะขึ้นกับ Hint เป็นพิเศษ: เมื่อไม่แน่ใจเกี่ยวกับการกด “ลบ” โดยไม่มีคำอธิบาย 40% จะปฏิเสธการกระทำ
ข้อผิดพลาดที่พบบ่อย: เขียน “ปุ่มลบ” ใน Label แทนที่จะเป็น “ลบ” ชนิดของอารม์ (ปุ่ม) จะถูกเพิ่มโดย VoiceOver โดยอัตโนมัติผ่านเครื่องหมาย ผลลัพธ์คือผู้ใช้จะได้ยิน: “ปุ่มลบ, ปุ่ม” — การซ้ำกัน Label ที่ถูกต้อง: “ลบ”, Hint: “ลบรูปภาพที่เลือก”
การเสริมภาษาลาเบล เป็นข้อบังคับ — ทำผ่านกลไกมาตรฐาน: NSLocalizedString ใน iOS, ทรัพยากรสตริง @string/ ใน Android อย่าตั้ง Label โดยการต่อคำเป็นภาษาอังกฤษโดยไม่มีการเสริมภาษา
กฎของ Label ที่ดี โดยอึงตาม W3C WCAG 2.2:
ใช้ อักษรานุกรมเดียวกัน สำหรับ Label ทั่วทั้งแอปพลิเคชัน หากหน้าจอหนึ่งเขียน “รายการโปรด” และอีกหน้าจอหนึ่งเขียน “บุ๊คมาร์ก” ผู้ใช้จะสับสน สร้างตารางคำศพท์การเข้าถึงได้ — ประสานงานกับนักออกแบบและนักเสริมภาษา
สำหรับ ช่องป้อนข้อมูล (UITextField, EditText) Label ต้องตรงกับข้อความที่ว่างไว้หรือป้ายกำกับช่อง อย่างไรก็ตาม ข้อความที่ว่างไว้มักจะหายไปหลังจากป้อนข้อมูล ใช้ accessibilityLabel สำหรับชื่อถาวร และ accessibilityValue สำหรับเนื้อหาปัจจุบันของช่อง — นี้คือมาตรฐาน WCAG 4.1.2 วิธีแก้: ตั้ง accessibilityLabel แบบคงที่ (เท่ากับป้ายกำกับช่อง) และ accessibilityValue แบบไดนามิก (เท่ากับข้อความที่ป้อนแล้ว) ใน iOS นี้เป็นอัตโนมัติ แต่สำหรับช่องที่กำหนดเอง — ด้วยตนเองโดยการเกินค่า accessibilityValue ตรวจสอบว่า VoiceOver อ่าน: “อีเมล, ตัวอย่าง@โดเมน.คอม, ช่องข้อความ” แทนที่จะเป็น “, ช่องข้อความ”
การทดสอบอัตโนมัติ เป็นวิธีเดียวเท่านั้นที่จะรับประกันความถูกต้องของ Label บนทุกหน้าจอ iOS ให้ XCUIApplication ที่สามารถเข้าถึง .label ได้ Android — AccessibilityCheckRule และ setContentDescription
ตัวอย่างการทดสอบสำหรับ iOS:
func testLabelsAreUnique() {
let app = XCUIApplication()
app.launch()
let allButtons = app.buttons.allElementsBoundByIndex
let labels = allButtons.compactMap { $0.label }
let uniqueLabels = Set(labels)
XCTAssertEqual(labels.count, uniqueLabels.count,
"พบ Label ซ้ำกัน")
}
ตัวอย่างสำหรับ Android กับ Espresso:
@Test
fun testButtonHasAccessibilityLabel() {
onView(withId(R.id.btnSubmit))
.check(matches(
withContentDescription(containsString("ส่ง"))
))
}
การทดสอบด้วยตนเอง: เปิด VoiceOver (iOS) หรือ TalkBack (Android) และปัดไปทางขวาผ่านองค์ประกอบทั้งหมดบนหน้าจอ แต่ละองค์ประกอบควรได้รับ การประกาศที่มีความหมาย หากคุณได้ยินเฉพาะ “ปุ่ม” หรือ “รูปภาพ” — แสดงว่า Label ขาดหาย
หลังจากตั้งค่า Label แล้ว ผู้ใช้ VoiceOver สามารถใช้ โรเตอร์ สำหรับการนำทางด่วน: โหมด “ปุ่ม”, “ส่วนหัว”, “ลิ้งค์” และอื่น หากตั้ง Label อย่างถูกต้อง VoiceOver จะรวมองค์ประกอบไว้ในโหมดโรเตอร์ที่เหมาะสม ตรวจสอบว่าปุ่มทั้งหมดแสดงในโหมด “ปุ่ม” และส่วนหัวทั้งหมดแสดงในโหมด “ส่วนหัว”
Label ยังมีผลต่อการค้นหาของ VoiceOver ผู้ใช้สามารถพิมพ์คำในโหมดค้นหาและ VoiceOver จะย้ายโฟกัสไปยังองค์ประกอบที่มี Label ตรงกัน ดังนั้น Label ควรมีคำสำคัญที่ผู้ใช้จะค้นหา
เพิ่มการตรวจสอบ Label ในไรเปอร์ไลน์ บน iOS ใช้ XCUITest กับ fastlane scan บน Android ใช้ Accessibility Test Framework กับกฏ AccessibilityCheckRule ที่ตรวจจับ contentDescription ที่ว่างเปล่า ซึ่งช่วยป้องกันการถดถอยเมื่อผสานหน้าจอใหม่
คำถามที่พบบ่อย
Label ระบุองค์ประกอบ (“ค้นหา”) Hint อธิบายผลลัพธ์ของการกระทำ (“เปิดหน้าจอค้นหา”) VoiceOver จะประกาศ Label ทันทีเมื่อได้โฟกัส และ Hint ในโหมด คำอธิบายโดยละเอียด
ใน iOS UILabel จะได้รับ accessibilityLabel ให้เท่ากับข้อความของมันโดยอัตโนมัติ ไม่จำเป็นต้องตั้งค่าเพิ่มเติม ใน Android TextView ทำงานเหมือนกัน
ตั้ง isAccessibilityElement = true บน View หลัก และเกินค่า accessibilityLabel เพื่อส่งคืนข้อความที่ต่อกันจากองค์ประกอบย่อย สำหรับองค์ประกอบที่ซับซ้อน ใช้การต่อคำด้วยตัวเข็งแยก
เพิ่ม บริบท ใส่องค์ประกอบที่ซ้ำกัน: “ซื้อ iPhone 15”, “ซื้อ iPhone 15 Pro” ทำให้การตรวจสอบเป็นอัตโนมัติผ่านการทดสอบ UI — รวบรวม Label ทั้งหมดและตรวจสอบว่าไม่มีข้อซ้ำกัน
ไม่ได้ หากต้องการซ่อนองค์ประกอบ ให้ใช้ isAccessibilityElement = false ใน iOS หรือ importantForAccessibility = “no” ใน Android Label ที่ว่างเปล่าจะไม่ซ่อนองค์ประกอบ — โปรแกรมอ่านหน้าจอจะอ่าน “ไม่มีชื่อ”
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม