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% از نقضهای بحرانی دسترسی در برنامههای فروشگاهی مربوط به نبود یا نادرستی Label است. Accessibility Scanner در Android نبود برچسب را به عنوان یک خطای با بالاترین شدت آشکار میکند.
محدودیت اصلی: Label نباید شامل نوع عنصر باشد. VoiceOver و TalkBack بهطور خودکار نقش (دکمه، سربرگ، پیوند) را به اعلان اضافه میکنند. اگر Label شامل «دکمه ارسال» باشد، کاربر میشنود: «دکمه ارسال، دکمه» — تکرار.
WCAG 4.1.2 (سطح A) ایجاب میکند که هر عنصر رابطی دارای نام (name)، نقش (role) و مقدار (value) قابل تعیین برنامهای باشد. Accessibility Label نام را تامین میکند. اگر Label وجود نداشته باشد، معیار نقض شده تلقی میشود و برنامه از گواهی اساسی عبور نمیکند.
در iOS، accessibilityLabel توسط همه UIViewها از پروتکل UIAccessibility به ارث میرسد. اگر عنصر حاوی متن باشد (UIButton با title، UILabel با text)، 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های سفارشی، میتوان getter 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 = "محصول در علاقهمندیها"
// برای نمایش سفارشی با چندین عنصر
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 از طریق trait بهطور خودکار اضافه میشود. در نتیجه، کاربر میشنود: «دکمه حذف، دکمه» — تکرار. Label درست: «حذف»، Hint: «عکس انتخاب شده را حذف میکند».
لوکالیسازی برچسبها اجباری است — از طریق مکانیسمهای استاندارد انجام میشود: NSLocalizedString در iOS، منابع رشتهای @string/ در Android. هرگز Label را بدون لوکالیسازی با انگلیسی ترکیب نکنید.
قوانین Label خوب، بر اساس W3C WCAG 2.2:
از واژهنامه واحد برای Label در برنامه استفاده کنید. اگر روی یک صفحه «علاقهمندیها» و روی صفحه دیگر «نشانگذاریها» نوشته شده، کاربر سرگردان میشود. یک جدول اصطلاحات دسترسی ایجاد کنید — با طراحان و لوکالیسازان هماهنگ کنید.
برای فیلدهای ورودی (UITextField، EditText)، Label باید با placeholder یا عنوان فیلد هماهنگ باشد. اما placeholder معمولاً پس از ورود متن مخفی میشود. از accessibilityLabel برای نام ثابت و accessibilityValue برای محتوای فعلی فیلد استفاده کنید — این استاندارد WCAG 4.1.2 است. راه حل: accessibilityLabel را بهصورت استاتیک (برابر با عنوان فیلد) و accessibilityValue را بهصورت دینامیک (برابر با متن وارد شده) تنظیم کنید. در iOS این بهطور خودکار انجام میشود، اما برای فیلدهای سفارشی — به صورت دستی از طریق بازنویسی accessibilityValue. بررسی کنید که VoiceOver به جای «، فیلد متنی»، «ایمیل، example@domain.com، فیلد متنی» را بخواند.
آزمایش خودکار تنها راه تضمین دقت 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 را به pipeline اضافه کنید. در 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 از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید