دسترسپذیری در .NET MAUI: راهنمای کامل SemanticProperties، VoiceOver و TalkBack در ۲۰۲۶
با پیکربندی SemanticProperties در .NET MAUI اپهای واقعاً دسترسپذیر بسازید. راهنمای عملی VoiceOver و TalkBack، انطباق با WCAG 2.2 و قانون EAA اروپا با نمونه کد آماده.
دسترسپذیری در .NET MAUI یعنی مجموعهای از APIهایی که با کمک SemanticProperties و AutomationProperties، اطلاعات معنایی کنترلها را به خوانندههای صفحه iOS (VoiceOver) و اندروید (TalkBack) میرسانند تا کاربران دارای معلولیت بتوانند اپ شما را بدون مانع استفاده کنند. راستش، تا وقتی یک کاربر نابینا با اپ من کار نکرد، نمیدانستم چقدر نکات ریز اهمیت دارند. از تیر ۱۴۰۴ (ژوئن ۲۰۲۵) هم قانون European Accessibility Act (EAA) در اتحادیه اروپا لازمالاجرا شده و انطباق با WCAG 2.2 برای اپلیکیشنهای تجاری موبایل الزامی است؛ در این راهنما قدمبهقدم یاد میگیرید یک اپ MAUI واقعاً دسترسپذیر بسازید.
SemanticProperties.Description، Hint و HeadingLevel سه ویژگی اصلی هستند که در .NET MAUI 9 و 10 مستقیماً روی VoiceOver و TalkBack نگاشت میشوند.
از تاریخ ۲۸ ژوئن ۲۰۲۵ قانون EAA در اتحادیه اروپا لازمالاجرا شد و انطباق با WCAG 2.2 سطح AA برای اپلیکیشنهای تجاری اجباری است.
حداقل نسبت کنتراست متن ۴.۵ به ۱ و اندازه هدف لمسی ۴۴×۴۴ نقطه در iOS و ۴۸×۴۸ dp در اندروید الزامی است (WCAG 2.5.5 و 2.5.8).
ابزارهای رسمی تست شامل Accessibility Inspector در Xcode 16 و Accessibility Scanner گوگل نسخه ۳ هستند که هر دو در چرخه CI/CD قابل ادغاماند.
پشتیبانی از مقیاسپذیری فونت با Dynamic Type در iOS و Font Scale در اندروید بدون شکستن چیدمان، معمولترین اشتباهی است که تیمها مرتکب میشوند.
مدیریت فوکوس (Focus، TabIndex) و ترتیب پیمایش منطقی برای کاربران کیبورد و سوییچکنترل حیاتی است.
چرا دسترسپذیری در ۲۰۲۶ اهمیت حیاتی دارد؟
صادقانه بگویم: در چند پروژه سازمانی که دسترسپذیری را در انتهای چرخه اضافه کردیم، هزینهاش تقریباً پنج برابر زمانی شد که از روز اول در معماری در نظر میگرفتیم (این را با چشم خودم دیدم). طبق گزارش سازمان بهداشت جهانی، بیش از ۱.۳ میلیارد نفر با نوعی معلولیت زندگی میکنند و هنوز ۹۶ درصد سایتهای پرترافیک استانداردهای WCAG را کامل رعایت نمیکنند. عدد کوچکی نیست.
از ۲۸ ژوئن ۲۰۲۵ قانون European Accessibility Act در ۲۷ کشور اتحادیه اروپا لازمالاجرا شد. این قانون شامل تمام اپلیکیشنهای موبایل تجاری، بانکداری، تجارت الکترونیک، حملونقل و ارتباطات میشود و جریمه عدم انطباق تا ۵ درصد از درآمد سالانه شرکت است. در ایالات متحده، عنوان III قانون ADA برای اپلیکیشنهای موبایل توسط دادگاههای فدرال به رسمیت شناخته شده و بیش از ۴۲۰۰ دعوی حقوقی در سال ۲۰۲۵ ثبت شده است. علاوه بر جنبههای حقوقی، رعایت دسترسپذیری بازار خود را حدود ۱۵ درصد گسترش میدهد و امتیاز App Store و Google Play را از طریق کاهش نرخ حذف اپ افزایش میدهد.
نکته مهم این است که مستندات رسمی .NET MAUI از نسخه ۸ به بعد یک لایه انتزاعی مشترک ارائه میدهد که بهطور خودکار به APIهای بومی UIAccessibility در iOS و AccessibilityNodeInfo در اندروید نگاشت میشود. با MAUI 10 که در نوامبر ۲۰۲۵ منتشر شد، پشتیبانی از HeadingLevel نیز بهطور کامل روی ویندوز و مککاتالیست فراهم است.
SemanticProperties در .NET MAUI چیست و چگونه کار میکند؟
SemanticProperties یک کلاس ایستا در فضاینام Microsoft.Maui.Controls است که سه ویژگی الحاقی (Attached Property) اصلی برای انتقال معنا به خواننده صفحه فراهم میکند. این ویژگیها در XAML یا کد C# روی هر عنصر بصری قابل تنظیم هستند و در زمان اجرا توسط لایه Handler به معادل بومی خود ترجمه میشوند.
سه ویژگی اصلی SemanticProperties
Description: متن کوتاهی که هدف عنصر را توصیف میکند؛ مثلاً برای یک آیکون سطل زباله «حذف پیام». معادل accessibilityLabel در iOS و contentDescription در اندروید.
Hint: راهنمایی برای نتیجه فعالسازی عنصر؛ مثلاً «برای حذف پیام دوبار ضربه بزنید». معادل accessibilityHint در iOS و کمکمتن TalkBack.
HeadingLevel: سطح سمانتیک هدینگ از Level1 تا Level9 که به کاربر VoiceOver اجازه پیمایش سریع بین بخشها با یک انگشت را میدهد.
در کد C# نیز میتوانید همین ویژگیها را بهصورت پویا تنظیم کنید که برای محتوایی که در زمان اجرا تولید میشود (مثلاً لیست نوتیفیکیشنها) بسیار کاربردی است. اگر با معماری MVVM کار میکنید، پیشنهاد میکنیم راهنمای معماری MVVM در .NET MAUI را نیز مطالعه کنید تا Binding این ویژگیها به ViewModel را در معماری صحیح جای دهید.
VoiceOver خواننده صفحه پیشفرض iOS است که در تمام دستگاههای اپل از iPhone 5s به بعد وجود دارد. طبق آمار اپل، حدود ۳.۸ درصد کاربران iOS از VoiceOver بهطور فعال استفاده میکنند. برای فعالسازی روی دستگاه: Settings → Accessibility → VoiceOver یا از میانبر سهبار فشردن دکمه کناری استفاده کنید.
در .NET MAUI هنگام کار با iOS، لایه Handler بهطور خودکار سه ویژگی SemanticProperties را به UIAccessibilityElement نگاشت میکند. با این حال، برای سناریوهای پیچیده (مانند سلولهای سفارشی در CollectionView) گاهی نیاز به دسترسی مستقیم به API بومی دارید که با استفاده از دستور شرطی پلتفرم امکانپذیر است.
#if IOS
using UIKit;
public partial class ProductCell : Grid
{
protected override void OnHandlerChanged()
{
base.OnHandlerChanged();
if (Handler?.PlatformView is UIView platformView)
{
platformView.IsAccessibilityElement = true;
platformView.AccessibilityTraits = UIAccessibilityTrait.Button;
platformView.AccessibilityLabel = $"{ProductName}, {Price} تومان";
platformView.AccessibilityHint = "برای مشاهده جزئیات محصول، دوبار ضربه بزنید";
}
}
}
#endif
گروهبندی عناصر مرتبط با ShouldGroupAccessibilityChildren
یکی از رایجترین مشکلات، اعلام جداگانه هر عنصر داخل یک کارت (مثلاً تصویر، عنوان، قیمت و دکمه خرید) توسط VoiceOver است که تجربه کاربری خستهکنندهای میسازد. با تنظیم ShouldGroupAccessibilityChildren روی والد، VoiceOver فرزندان را بهعنوان یک واحد اعلام میکند.
#if IOS
if (Handler?.PlatformView is UIView view)
{
view.ShouldGroupAccessibilityChildren = true;
view.AccessibilityLabel = "کارت محصول: هدفون بیسیم سونی، قیمت ۴ میلیون و ۲۰۰ هزار تومان";
view.AccessibilityTraits = UIAccessibilityTrait.Button;
}
#endif
پشتیبانی از TalkBack در اندروید
TalkBack خواننده صفحه گوگل است که در تمام دستگاههای اندروید ۶ به بالا از پیش نصب شده و در نسخه Android 15 با اضافهشدن قابلیت Gemini AI-powered descriptions توانمندتر شده است. برای فعالسازی: Settings → Accessibility → TalkBack. حرکتهای اصلی TalkBack شامل ضربه با یک انگشت برای انتخاب و دوبار ضربه برای فعالسازی است.
در .NET MAUI، Handler اندروید SemanticProperties.Description را به ContentDescription و SemanticProperties.Hint را به ViewCompat.SetAccessibilityDelegate نگاشت میکند. برای عناصر تعاملی سفارشی که از ContentView ارثبری میکنند، حتماً Focusable را روی true تنظیم کنید تا TalkBack بتواند فوکوس بگیرد.
#if ANDROID
using Android.Views;
using AndroidX.Core.View;
using AndroidX.Core.View.Accessibility;
public partial class RatingStar : ContentView
{
protected override void OnHandlerChanged()
{
base.OnHandlerChanged();
if (Handler?.PlatformView is Android.Views.View androidView)
{
androidView.Focusable = true;
androidView.ImportantForAccessibility = ImportantForAccessibility.Yes;
ViewCompat.SetAccessibilityDelegate(androidView,
new AccessibilityDelegateCompat
{
// اضافهکردن اکشن سفارشی «افزایش امتیاز»
});
ViewCompat.ReplaceAccessibilityAction(
androidView,
AccessibilityNodeInfoCompat.AccessibilityActionCompat.ActionClick,
"تغییر امتیاز",
null);
}
}
}
#endif
Live Regions برای بهروزرسانیهای پویا
در اپلیکیشنهای چت یا نوتیفیکیشن، وقتی محتوایی بهطور خودکار بهروز میشود (مثل رسیدن پیام جدید یا نمایش خطای فرم)، باید از Live Region استفاده کنید تا TalkBack بدون نیاز به فوکوس، تغییرات را اعلام کند.
#if ANDROID
if (errorLabel.Handler?.PlatformView is Android.Views.View errView)
{
ViewCompat.SetAccessibilityLiveRegion(
errView,
ViewCompat.AccessibilityLiveRegionPolite);
}
#endif
در سناریوهای چندزبانه که کاربر ممکن است اپ را به فارسی یا انگلیسی استفاده کند، رعایت جهت متن (RTL) در متنهای سمانتیک حیاتی است. برای درک عمیقتر پیشنهاد میکنیم به راهنمای محلیسازی و پشتیبانی RTL در .NET MAUI مراجعه کنید.
مدیریت فوکوس و ترتیب پیمایش صحیح
ترتیب فوکوس یکی از مشکلات پنهان اپلیکیشنهای MAUI است. بهطور پیشفرض، ترتیب پیمایش بر اساس ترتیب اضافهشدن به درخت بصری است، اما در Grid یا چیدمانهای پیچیده این ترتیب ممکن است با ترتیب منطقی مطابق نباشد. برای کنترل صریح از ویژگی TabIndex استفاده کنید.
برای پنهان کردن عناصر تزئینی (مثل خطوط جداکننده یا آیکونهای زیباییشناختی) از IsInAccessibleTree="False" استفاده کنید. این جلوگیری میکند از اینکه خواننده صفحه روی چیزی که هیچ ارزش معنایی ندارد وقت هدر دهد.
هنگام باز شدن یک صفحه جدید، طبق راهنمای WCAG 2.4.3، فوکوس باید به عنصر معنایی اول (معمولاً عنوان صفحه) منتقل شود. متد SetSemanticFocus() که در MAUI 8 اضافه شد این کار را انجام میدهد:
public partial class OrderConfirmationPage : ContentPage
{
protected override void OnAppearing()
{
base.OnAppearing();
// انتقال فوکوس به عنوان صفحه پس از بارگذاری
Dispatcher.Dispatch(() =>
{
PageHeading.SetSemanticFocus();
});
}
}
کنتراست رنگ، مقیاسپذیری فونت و Dynamic Type
طبق WCAG 2.1 معیار ۱.۴.۳، نسبت کنتراست بین متن و پسزمینه باید حداقل ۴.۵ به ۱ برای متن معمولی و ۳ به ۱ برای متن بزرگ (بالای ۱۸ پوینت) باشد. برای متنهای اصلی رابط کاربری، هدف را روی سطح AAA یعنی ۷ به ۱ تنظیم کنید تا در نور خورشید و صفحههای ضعیف نیز خوانا باشد.
معیار WCAG 2.2
سطح AA (الزامی)
سطح AAA (توصیهشده)
ابزار سنجش
کنتراست متن معمولی
۴.۵ : ۱
۷ : ۱
Stark, Contrast App
کنتراست متن بزرگ (۱۸pt+)
۳ : ۱
۴.۵ : ۱
Accessibility Inspector
کنتراست عناصر گرافیکی
۳ : ۱
–
Colour Contrast Analyser
اندازه هدف لمسی iOS
۴۴×۴۴ pt
۴۸×۴۸ pt
Accessibility Inspector
اندازه هدف لمسی اندروید
۴۸×۴۸ dp
۵۶×۵۶ dp
Accessibility Scanner
مقیاسپذیری فونت
۲۰۰٪
۲۰۰٪ بدون شکست چیدمان
Simulator Settings
پشتیبانی از Dynamic Type در MAUI
در iOS کاربران میتوانند از تنظیمات، اندازه فونت سیستم را تا ۳۱۰ درصد بزرگ کنند. .NET MAUI با FontAutoScalingEnabled="True" بهطور پیشفرض این را احترام میگذارد، اما باید مطمئن شوید که چیدمان شما با فونتهای بزرگ نیز کار میکند.
<Label Text="خوش آمدید"
FontSize="18"
FontAutoScalingEnabled="True"
LineBreakMode="WordWrap" />
<!-- استفاده از StackLayout بهجای فیکسکردن ارتفاع -->
<VerticalStackLayout>
<Label Text="مبلغ سفارش"
FontAutoScalingEnabled="True" />
<Label Text="۴۵۰,۰۰۰ تومان"
FontSize="24"
FontAutoScalingEnabled="True" />
</VerticalStackLayout>
تست دسترسپذیری با Accessibility Inspector و Accessibility Scanner
تست دسترسپذیری باید بخشی از چرخه توسعه باشد، نه فقط یک بازبینی نهایی. سه لایه تست پیشنهاد میشود: تست دستی با خواننده صفحه واقعی، اسکن خودکار با ابزارهای رسمی پلتفرم، و ادغام تستهای خودکار در CI/CD.
Accessibility Inspector در Xcode 16
این ابزار در مسیر Xcode → Open Developer Tool → Accessibility Inspector قرار دارد و در Xcode 16 قابلیت جدید Audit Automation اضافه شده که کل درخت UI را برای مشکلات دسترسپذیری اسکن میکند و گزارش JSON خروجی میدهد. برای شبیهساز، شبیهساز مقصد را انتخاب و روی «Run Audit» کلیک کنید.
Accessibility Scanner گوگل
این اپلیکیشن رسمی گوگل که از Play Store قابل نصب است، اسکرینشاتهای اپلیکیشن شما را تحلیل میکند و مشکلات کنتراست، اندازه هدف لمسی و برچسبهای گمشده را با پیشنهاد اصلاحی نمایش میدهد. برای اپلیکیشنهای تجاری، نتیجه اسکن را در قالب گزارش HTML خروجی بگیرید و در PRها ضمیمه کنید.
تست خودکار دسترسپذیری در Appium
برای ادغام در پایپلاین CI، از appium-accessibility-plugin نسخه ۲.۳ استفاده کنید که در ۲۰۲۵ منتشر شد و مستقیماً با فریمورک تست شما در .NET MAUI ادغام میشود. جزئیات کامل تستنویسی UI را در راهنمای تستنویسی در .NET MAUI با Appium مطالعه کنید.
[Test]
public async Task LoginButton_ShouldHaveAccessibilityLabel()
{
var driver = _appiumFixture.CreateAndroidDriver();
var loginButton = driver.FindElement(By.Id("LoginButton"));
var contentDescription =
loginButton.GetAttribute("content-desc");
Assert.That(contentDescription, Is.Not.Null.And.Not.Empty,
"دکمه ورود باید ContentDescription داشته باشد");
// اطمینان از اندازه هدف لمسی
var size = loginButton.Size;
Assert.That(size.Width, Is.GreaterThanOrEqualTo(48));
Assert.That(size.Height, Is.GreaterThanOrEqualTo(48));
}
چکلیست انطباق با WCAG 2.2 برای .NET MAUI
WCAG 2.2 که در اکتبر ۲۰۲۳ منتشر شد، ۹ معیار جدید نسبت به نسخه ۲.۱ دارد که چهار مورد آنها بهطور مستقیم روی اپلیکیشنهای موبایل تأثیر میگذارند. جدول زیر معیارهای اصلی و نگاشت آنها به APIهای .NET MAUI را نشان میدهد:
معیارهای Level A و AA اجباری
۱.۱.۱ محتوای غیرمتنی: هر Image، ImageButton و آیکون باید SemanticProperties.Description داشته باشد یا با IsInAccessibleTree="False" از درخت خارج شود.
۱.۳.۱ اطلاعات و روابط: از SemanticProperties.HeadingLevel برای ساختاردهی صفحه استفاده کنید.
۱.۴.۳ کنتراست حداقل: نسبت ۴.۵ به ۱ برای تمام متون معمولی رعایت شود.
۱.۴.۴ تغییر اندازه متن: اپلیکیشن باید با فونت ۲۰۰٪ همچنان قابل استفاده باشد.
۲.۱.۱ کیبورد: تمام عملکردها با کیبورد خارجی (bluetooth) و سوییچکنترل قابل دسترسی باشند.
۲.۴.۳ ترتیب فوکوس: ترتیب TabIndex باید با ترتیب منطقی خواندن مطابقت داشته باشد.
۲.۵.۵ اندازه هدف لمسی: حداقل ۴۴×۴۴ pt در iOS و ۴۸×۴۸ dp در اندروید (معیار جدید WCAG 2.2 → 2.5.8).
۳.۳.۲ برچسب یا دستورالعمل: هر Entry باید Label بصری و SemanticProperties.Description داشته باشد.
۴.۱.۲ نام، نقش، مقدار: با استفاده از AutomationProperties.LabeledBy و Description اطلاعات کامل ارسال شود.
برای مستندسازی رسمی سیاست دسترسپذیری اپلیکیشن، یک بیانیه Accessibility Statement طبق نمونه ابزار رسمی W3C تهیه کنید و در بخش «درباره ما» یا تنظیمات اپ قرار دهید. این کار در بسیاری از قوانین از جمله EAA اجباری است.
اشتباهات رایج و راهحل عملی آنها
در بازبینی کد بیش از سی پروژه MAUI، همین چند الگوی اشتباه مدام تکرار میشوند. هر کدام هم راهحل سادهای دارند، اگر بدانیم کجا را نگاه کنیم:
۱. استفاده از تصویر بهجای متن برای دکمهها
دکمههای آیکونمحور (مثل قلب لایک یا آیکون اشتراکگذاری) اگر بدون SemanticProperties.Description باشند، برای کاربران خواننده صفحه کاملاً نامرئی میشوند. راهحل: همیشه توصیف معنایی اضافه کنید و در صورت تغییر حالت (مثل «لایک شد»)، مقدار Description را بهروز کنید.
۲. استفاده از رنگ بهعنوان تنها نشانگر
نمایش خطای فرم فقط با تغییر رنگ حاشیه به قرمز، برای کاربران کوررنگ (حدود ۸ درصد مردان) قابل تشخیص نیست. راهحل: علاوه بر رنگ، از آیکون خطا و متن راهنما استفاده کنید.
۳. متنهای Placeholder بهعنوان Label
Placeholder یک راهنمای گذرا است و پس از تایپ کاربر ناپدید میشود. راهحل: برای هر Entry یک Label بالای آن اضافه کنید و از AutomationProperties.LabeledBy برای پیوند دو عنصر استفاده کنید.
وقتی داده در حال بارگذاری است، کاربر خواننده صفحه نمیداند صفحه فریز شده یا اپ در حال کار است. راهحل: از SemanticScreenReader.Default.Announce() برای اعلام رویدادهای مهم استفاده کنید.
public async Task LoadDataAsync()
{
SemanticScreenReader.Default.Announce(
"در حال دریافت اطلاعات، لطفاً منتظر بمانید");
var result = await _apiService.GetOrdersAsync();
SemanticScreenReader.Default.Announce(
$"{result.Count} سفارش بارگذاری شد");
}
۵. غفلت از تست با کاربران واقعی
هیچ ابزار خودکاری جای تست با کاربران واقعی نابینا یا کمبینا را نمیگیرد. راهحل: با انجمنهای نابینایان محلی همکاری کنید و حداقل یک جلسه Usability Test قبل از هر Release بزرگ برگزار کنید. مستندات راهنمای تست دسترسپذیری اندروید نمونه پروتکل تست را ارائه میدهد.
پرسشهای متداول
آیا .NET MAUI بهطور کامل از دسترسپذیری پشتیبانی میکند؟
بله، از نسخه ۸ به بعد .NET MAUI یک لایه انتزاعی مشترک با SemanticProperties و AutomationProperties ارائه میدهد که بهطور خودکار به VoiceOver در iOS، TalkBack در اندروید، Narrator در ویندوز و VoiceOver در macOS نگاشت میشود. با نسخه ۱۰ که نوامبر ۲۰۲۵ منتشر شد، پشتیبانی از HeadingLevel و SetSemanticFocus روی تمام پلتفرمها کامل است.
تفاوت SemanticProperties و AutomationProperties در .NET MAUI چیست؟
SemanticProperties برای انتقال اطلاعات به خوانندگان صفحه (VoiceOver، TalkBack) طراحی شده و شامل Description، Hint و HeadingLevel است. در مقابل، AutomationProperties بیشتر برای تستهای UI خودکار (Appium، Xamarin.UITest) استفاده میشود و شامل ویژگیهایی مانند IsInAccessibleTree، LabeledBy و Name است. در عمل هر دو مکمل یکدیگر هستند.
قانون European Accessibility Act چه تأثیری روی اپلیکیشن موبایل من دارد؟
از ۲۸ ژوئن ۲۰۲۵، تمام اپلیکیشنهای موبایل تجاری که در ۲۷ کشور اتحادیه اروپا عرضه میشوند باید با WCAG 2.2 سطح AA منطبق باشند. این شامل بانکداری، تجارت الکترونیک، حملونقل، رزرو بلیت و ارتباطات میشود. شرکتهای خارج از اتحادیه اروپا نیز اگر اپشان در بازار EU در دسترس باشد مشمول هستند. جریمهها بسته به کشور تا ۵ درصد درآمد سالانه یا ۵۰۰,۰۰۰ یورو است.
چگونه اپلیکیشن MAUI خود را با VoiceOver تست کنم؟
برای تست روی iPhone فیزیکی، از Settings → Accessibility → VoiceOver فعال کنید یا میانبر سهبار فشردن دکمه کناری را تنظیم کنید. برای تست در شبیهساز، از Accessibility Inspector در Xcode 16 استفاده کنید (Xcode → Open Developer Tool → Accessibility Inspector) که Audit خودکار روی صفحه فعلی اجرا میکند و مشکلات را با پیشنهاد اصلاحی گزارش میدهد.
آیا اندازه هدف لمسی در .NET MAUI بهطور خودکار رعایت میشود؟
خیر، MAUI اندازه پیشفرض کنترلها را بر اساس محتوا تنظیم میکند. برای رعایت معیار WCAG 2.5.8 باید صراحتاً MinimumHeightRequest="44" و MinimumWidthRequest="44" روی iOS و ۴۸ dp روی اندروید تنظیم کنید. استفاده از Padding اضافی روی دکمههای کوچک نیز یک راه عملی برای افزایش سطح لمسی بدون تغییر ظاهر بصری است.
از resx و IStringLocalizer تا FlowDirection برای RTL، تقویم شمسی با PersianCalendar، فونت وزیرمتن، تبدیل اعداد و آینهسازی آیکونها — راهنمای کامل و عملی فارسیسازی اپ .NET MAUI 9 با کد قابل کپی برای اندروید و iOS.