Accessibility Label — định nghĩa, khái niệm cơ bản và cách sử dụng cho iOS và Android

Tác giả: IT Sectr Đã đăng: 2026-05-16 Thời gian đọc: 9 phút

Accessibility Label là tên của một phần tử giao diện mà VoiceOver (iOS) hoặc TalkBack (Android) đọc khi được tiêu điểm. Trên iOS, thuộc tính này được gọi là accessibilityLabel, trên Android — contentDescription cho các phần tử không chứa văn bản. Theo Apple Developer Documentation, 2024, nhãn là nền tảng của khả năng tiếp cận: không có nó, người dùng không thể xác định phần tử. Nhãn phải là duy nhất trong màn hình và phản ánh bản chất của phần tử bằng ngôn ngữ rõ ràng.

Điểm chính

  • Accessibility Label — tên của phần tử được trình đọc màn hình đọc; được đặt qua accessibilityLabel trên iOS và contentDescription trên Android
  • Nhãn phải khớp với văn bản hiển thị của phần tử hoặc thay thế nó cho các thành phần không phải văn bản
  • Mỗi nhãn phải duy nhất trong màn hình — nhãn trùng lặp làm người dùng mất phương hướng
  • Bản địa hóa nhãn là bắt buộc: nhãn được dịch sang tất cả ngôn ngữ được hỗ trợ của ứng dụng
  • Đối với các điều khiển tùy chỉnh, nhãn được đặt bằng lệnh thông qua ghi đè thuộc tính hoặc giao thức NSObject

Accessibility Label là gì

Accessibility Label là một thuộc tính chuỗi xác định tên của phần tử cho công nghệ hỗ trợ. Khi người dùng vuốt trên màn hình với VoiceOver được bật, trình đọc màn hình sẽ đọc nhãn của phần tử được tiêu điểm. Không có nhãn, người dùng chỉ nghe được loại phần tử: “nút”, “hình ảnh” — mà không biết mục đích.

Theo Google I/O 2024, “Accessibility Testing”, 35% vi phạm trợ năng nghiêm trọng trong các ứng dụng cửa hàng liên quan đến việc thiếu hoặc không chính xác của nhãn. Accessibility Scanner trên Android phát hiện thiếu nhãn như một lỗi nghiêm trọng nhất.

Một giới hạn cơ bản: Nhãn không được chứa loại phần tử. VoiceOver và TalkBack tự động thêm vai trò (nút, tiêu đề, liên kết) vào thông báo. Nếu nhãn chứa “Nút gửi”, người dùng sẽ nghe: “Nút gửi, nút” — trùng lặp.

Label và WCAG 4.1.2: Tên, Vai trò, Giá trị

WCAG 4.1.2 (cấp A) yêu cầu mỗi phần tử giao diện người dùng phải có tên, vai trò và giá trị có thể xác định bằng lệnh. Accessibility Label cung cấp tên. Nếu thiếu nhãn, tiêu chí bị coi là vi phạm và ứng dụng không đạt chứng nhận cơ bản.

iOS: thuộc tính accessibilityLabel

Trên iOS, accessibilityLabel được kế thừa bởi tất cả UIView từ giao thức UIAccessibility. Nếu phần tử chứa văn bản (UIButton có tiêu đề, UILabel có văn bản), nhãn sẽ tự động được đặt thành văn bản đó. Đối với UIImageView, các điều khiển tùy chỉnh và container, nhãn cần được đặt thủ công.

Ví dụ cho ô bảng tùy chỉnh:

swift
class CustomTableViewCell: UITableViewCell {
    let titleLabel = UILabel()
    let priceLabel = UILabel()

    override func awakeFromNib() {
        super.awakeFromNib()
        self.isAccessibilityElement = true
        self.accessibilityLabel =
            "\(titleLabel.text ?? "") - \(priceLabel.text ?? "")"
    }
}

Đối với UIView tùy chỉnh, bạn có thể ghi đè trình truy xuất accessibilityLabel:

swift
class RatingView: UIView {
    var rating: Int = 5

    override var accessibilityLabel: String? {
        get { return "Đánh giá: \(rating) trên 5" }
        set {}
    }
}

Apple HIG, 2024 khuyên: nếu một phần tử bao gồm nhiều phần tử con (ví dụ: thẻ sản phẩm có tên và giá), hãy kết hợp chúng thành một phần tử trợ năng duy nhất với nhãn tổng hợp. Đặt isAccessibilityElement = true trên phần tử cha và false trên các phần tử con.

NSAttributedString và accessibilityLabel

Nếu UILabel sử dụng NSAttributedString, accessibilityLabel mặc định bằng .string (văn bản thuần). Nếu bạn cần truyền một giá trị khác về mặt ngữ nghĩa (ví dụ: biểu tượng ký hiệu được đọc là “Ngôi sao” thay vì ký tự ★), hãy đặt accessibilityLabel một cách tường minh. VoiceOver không đọc các ký tự Unicode một cách có ý nghĩa.

Android: Label qua contentDescription

Trên Android, contentDescription đóng vai trò là nhãn cho ImageView, ImageButton và các View tùy chỉnh. Đối với TextView và Button có văn bản tích hợp, không cần đặt contentDescription — TalkBack đọc văn bản tự động.

Đặt bằng lệnh qua Kotlin:

kotlin
binding.iconStar.contentDescription = "Sản phẩm yêu thích"

// Cho View tùy chỉnh với nhiều phần tử
binding.customCard.setContentDescription(
    "\(title) với giá \(price)")

Trong XML cho các phần tử trang trí:

xml
<ImageView
    android:contentDescription="@null"
    android:src="@drawable/divider"
    android:importantForAccessibility="no" />

Thuộc tính importantForAccessibility = “no” loại bỏ hoàn toàn phần tử khỏi cây trợ năng. Trên iOS, tương đương là isAccessibilityElement = false.

Compose: semantics và contentDescription

Trong Jetpack Compose, nhãn được đặt qua bộ sửa đổi semantics:

kotlin
Image(
    painter = painterResource(R.drawable.ic_search),
    contentDescription = "Tìm kiếm sản phẩm",
    modifier = Modifier.semantics {
        contentDescription = "Tìm kiếm sản phẩm"
    }
)

Trong Compose, contentDescription là tham số bắt buộc cho Image — không có nó, mã sẽ không biên dịch được (cảnh báo). Điều này cải thiện khả năng tiếp cận một cách bắt buộc thông qua thiết kế API.

Label và Hint: khác biệt vai trò

Accessibility Label trả lời câu hỏi “Phần tử này là gì?”. Hint (accessibilityHint trong iOS, văn bản bổ sung trong contentDescription trong Android) — “Điều gì sẽ xảy ra khi tương tác?”. VoiceOver đọc chúng theo trình tự: đầu tiên là Label, sau đó là Hint.

Ví dụ cho nút xóa:

  • Label: “Xóa”
  • Hint: “Xóa vĩnh viễn ảnh đã chọn”
  • VoiceOver: “Xóa. Xóa vĩnh viễn ảnh đã chọn”

Theo Deque University, 2024, việc phân tách đúng đắn Label và Hint cải thiện tỷ lệ hoàn thành tác vụ cho người dùng VoiceOver lên 28%. Người dùng khiếm khuyết về nhận thức đặc biệt phụ thuộc vào Hint: khi không chắc chắn về việc nhấn “Xóa” mà không có giải thích, 40% từ chối hành động.

Khi nào không cần Hint

  • Phần tử có hành động trực quan (“Quay lại”, “Đóng” — Label đủ)
  • Label đã mô tả kết quả (“Gửi tin nhắn” — động từ trong chính tên)
  • Điều khiển hệ thống (UISwitch, UIButton với loại hệ thống) — hành vi của chúng là tiêu chuẩn

Lỗi thường gặp: Label thay vì Hint

Một lỗi thường gặp: viết “Nút xóa” trong Label thay vì “Xóa”. Loại phần tử (Nút) được VoiceOver tự động thêm vào thông qua một đặc điểm. Kết quả là người dùng nghe: “Nút xóa, nút” — trùng lặp. Label đúng: “Xóa”, Hint: “Xóa ảnh đã chọn”.

Bản địa hóa và thực hành tốt nhất

Bản địa hóa nhãn là bắt buộc — nó được thực hiện thông qua các cơ chế tiêu chuẩn: NSLocalizedString trong iOS, tài nguyên chuỗi @string/ trong Android. Không bao giờ đặt nhãn bằng cách nối chuỗi tiếng Anh mà không bản địa hóa.

Các quy tắc cho nhãn tốt, dựa trên W3C WCAG 2.2:

  • Bắt đầu bằng từ khóa — “Tìm kiếm sản phẩm”, không phải “Trường để tìm kiếm sản phẩm”
  • Không bao gồm từ “nút”, “trường”, “hình ảnh” — vai trò được thêm tự động
  • Sử dụng ngôn ngữ tự nhiên mà đối tượng mục tiêu có thể hiểu
  • Tránh viết tắt (trừ các từ thường được chấp nhận: “cái”, “kg”) — trình đọc màn hình đọc chúng theo nghĩa đen
  • Đối với phần tử nhập, thêm ví dụ: “Email (vidu@tenmien.com)”

Tính nhất quán của nhãn trong thương hiệu

Sử dụng một bảng thuật ngữ duy nhất cho nhãn trong toàn bộ ứng dụng. Nếu một màn hình ghi “Yêu thích” và màn hình khác ghi “Dấu trang”, người dùng sẽ bị mất phương hướng. Tạo một bảng thuật ngữ trợ năng — phối hợp với nhà thiết kế và người bản địa hóa.

Nhãn cho phần tử biểu mẫu

Đối với trường nhập liệu (UITextField, EditText), nhãn phải khớp với placeholder hoặc nhãn trường. Tuy nhiên, placeholder thường biến mất sau khi nhập văn bản. Sử dụng accessibilityLabel cho tên cố định và accessibilityValue cho nội dung hiện tại của trường — đây là tiêu chuẩn WCAG 4.1.2. Giải pháp: đặt accessibilityLabel tĩnh (bằng nhãn trường) và accessibilityValue động (bằng văn bản đã nhập). Trên iOS, điều này tự động, nhưng đối với các trường tùy chỉnh, hãy ghi đè accessibilityValue theo cách thủ công. Kiểm tra xem VoiceOver có đọc: “Email, vidu@tenmien.com, trường văn bản” thay vì “, trường văn bản”.

Cách kiểm tra nhãn trợ năng

Kiểm tra tự động là cách duy nhất để đảm bảo tính chính xác của nhãn trên tất cả màn hình. iOS cung cấp XCUIApplication với quyền truy cập .label, Android — AccessibilityCheckRule và setContentDescription.

Ví dụ kiểm tra cho iOS:

swift
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,
        "Đã tìm thấy nhãn trùng lặp")
}

Ví dụ cho Android với Espresso:

kotlin
@Test
fun testButtonHasAccessibilityLabel() {
    onView(withId(R.id.btnSubmit))
        .check(matches(
            withContentDescription(containsString("Gửi"))
        ))
}

Kiểm tra thủ công: bật VoiceOver (iOS) hoặc TalkBack (Android) và vuốt sang phải qua tất cả các phần tử trên màn hình. Mỗi phần tử phải nhận được thông báo có ý nghĩa. Nếu bạn chỉ nghe “nút” hoặc “hình ảnh” — nhãn đang bị thiếu.

Rotor VoiceOver và điều hướng nhanh

Sau khi thiết lập nhãn, người dùng VoiceOver có thể sử dụng rotor để điều hướng nhanh: các chế độ “Nút”, “Tiêu đề”, “Liên kết” và các chế độ khác. Nếu nhãn được đặt đúng, VoiceOver sẽ bao gồm phần tử trong chế độ rotor tương ứng. Kiểm tra xem tất cả các nút có hiển thị trong chế độ “Nút” và tất cả các tiêu đề trong chế độ “Tiêu đề” không.

Nhãn cũng ảnh hưởng đến tìm kiếm VoiceOver. Người dùng có thể nhập một từ trong chế độ tìm kiếm và VoiceOver sẽ di chuyển tiêu điểm đến phần tử có nhãn phù hợp. Do đó, nhãn nên chứa các từ khóa mà người dùng sẽ tìm kiếm.

Tích hợp vào đường ống CI/CD

Thêm kiểm tra nhãn vào đường ống. Trên iOS, sử dụng XCUITest với fastlane scan. Trên Android, sử dụng Accessibility Test Framework với quy tắc AccessibilityCheckRule phát hiện contentDescription trống. Điều này ngăn chặn các hồi quy khi hợp nhất các màn hình mới.

Câu hỏi thường gặp

Accessibility Label khác Accessibility Hint như thế nào?

Label xác định phần tử (“Tìm kiếm”), Hint giải thích kết quả hành động (“Mở màn hình tìm kiếm”). VoiceOver đọc Label ngay khi tiêu điểm, và Hint trong chế độ mô tả chi tiết.

Có cần đặt Label cho UILabel có văn bản không?

Trên iOS, UILabel tự động nhận được accessibilityLabel bằng văn bản của nó. Không cần thiết lập thêm. Trên Android, TextView hoạt động tương tự.

Làm thế nào để đặt Label cho UIView tùy chỉnh?

Đặt isAccessibilityElement = true trên View cha và ghi đè accessibilityLabel để trả về văn bản nối từ các phần tử con. Đối với các thành phần phức tạp, sử dụng nối với dấu phân cách.

Làm thế nào để tránh nhãn trùng lặp trên màn hình?

Thêm ngữ cảnh vào các phần tử lặp lại: “Mua iPhone 15”, “Mua iPhone 15 Pro”. Tự động hóa kiểm tra thông qua kiểm thử UI — thu thập tất cả nhãn và kiểm tra không có trùng lặp.

Tôi có thể sử dụng Label để ẩn phần tử khỏi trình đọc màn hình không?

Không. Để ẩn phần tử, sử dụng isAccessibilityElement = false trên iOS hoặc importantForAccessibility = “no” trên Android. Nhãn trống không ẩn được phần tử — trình đọc màn hình sẽ đọc “không có tiêu đề”.

Tổng kết

  • Accessibility Label — tên của phần tử cho VoiceOver và TalkBack; được đặt qua accessibilityLabel trên iOS và contentDescription trên Android
  • Nhãn phải khớp với văn bản hiển thị của phần tử văn bản; đối với phần tử không phải văn bản (biểu tượng, hình ảnh) được đặt thủ công
  • Hint trả lời “Điều gì sẽ xảy ra?” và không lặp lại Label — các thuộc tính này có vai trò khác nhau
  • Mỗi nhãn phải duy nhất trên màn hình; trùng lặp làm người dùng trình đọc màn hình mất phương hướng
  • Bản địa hóa nhãn bắt buộc qua NSLocalizedString (iOS) và @string (Android)
  • Kiểm tra nhãn tự động qua kiểm thử UI (XCUIApplication, AccessibilityCheckRule) và thủ công qua VoiceOver
  • Ẩn các phần tử trang trí qua isAccessibilityElement = false hoặc importantForAccessibility = “no”

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