Focus Order — چیست، اصول و نحوه تنظیم آن در برنامه‌های موبایل

نویسنده: IT Sectr منتشر شده: 2026-05-16 زمان مطالعه: 9 دقیقه

Focus Order دنباله‌ای است که در آن عناصر رابط کاربری هنگام پیمایش با صفحه‌کلید، Switch Control، VoiceOver یا TalkBack فوکوس دریافت می‌کنند. در برنامه‌های موبایل، ترتیب فوکوس تعیین می‌کند که کاربر چگونه بین کنترل‌ها با حرکات یا دکمه‌ها جابه‌جا می‌شود. طبق W3C WCAG 2.2, Success Criterion 2.4.3, 2023، فوکوس باید در ترتیب منطقی‌ای دنبال شود که معنای محتوا را حفظ کند. نقض این اصل یکی از دلایل رایج عدم پذیرش ممیزی دسترس‌پذیری است.

نکات کلیدی

  • Focus Order — ترتیب پیمایش عناصر تعاملی هنگام پیمایش با صفحه‌کلید یا screen reader
  • فوکوس باید از ترتیب بصری (از چپ به راست، از بالا به پایین) پیروی کند و منطق محتوا را حفظ کند
  • در iOS ترتیب از طریق shouldGroupAccessibilityElement و آرایه accessibilityElements تنظیم می‌شود
  • در Android ویژگی‌های nextFocusDown، nextFocusUp، nextFocusLeft، nextFocusRight همسایگان فوکوس را تعیین می‌کنند
  • صفحه‌های سفارشی (نقشه‌ها، بوم‌ها، بازی‌ها) نیاز به مدیریت برنامه‌نویسی‌شده فوکوس از طریق UIAccessibilityPostNotification دارند

Focus Order در دسترس‌پذیری چیست

Focus Order دنباله‌ای است که در آن کاربر با روش‌های ورودی جایگزین بین عناصر تعاملی جابه‌جا می‌شود: صفحه‌کلید (Tab)، Switch Control (قدم به قدم)، VoiceOver (حرکت به راست/چپ) یا TalkBack. برخلاف ماوس یا صفحه لمسی که کاربر عنصر را مستقیماً انتخاب می‌کند، پیمایش فوکوس خطی است — هر قدم فوکوس را به عنصر بعدی می‌برد.

طبق Apple HIG, 2024، VoiceOver از ترتیب عناصر در درخت دسترس‌پذیری استفاده می‌کند که بر اساس چیدمان بصری ساخته می‌شود: گوشه بالا سمت چپ → گوشه پایین سمت راست. اگر صفحه شامل چیدمان پیچیده‌ای باشد (ستون‌ها، Grid، ZStack)، درخت ممکن است با ترتیب بصری مطابقت نداشته باشد.

اصل WCAG 2.4.3: «اگر صفحه وب را بتوان به‌صورت متوالی توسط بخش‌ها پیمایش کرد و ترتیب فوکوس بر معنا تأثیر بگذارد، فوکوس باید در ترتیبی دنبال شود که معنا و امکان کنترل را حفظ کند». استثنا: محتوای پویا که فوکوس می‌تواند برای جلب توجه بپرد (هشدارها، پنجره‌های مدال).

چرا Focus Order برای دسترس‌پذیری حیاتی است

کاربر Switch Control (افراد دارای اختلالات حرکتی) به‌صورت خودکار بین عناصر جابه‌جا می‌شود — چرخه به چرخه. اگر ترتیب نقض شود، کاربر 3 برابر زمان بیشتری برای تکمیل فرم صرف می‌کند. طبق Deque University, 2024، Focus Order صحیح زمان پر کردن فرم را برای کاربران فناوری‌های کمکی 60٪ کاهش می‌دهد.

Focus Order و پنجره‌های مدال

توجه ویژه به پنجره‌های مدال. پس از باز شدن پنجره مدال، فوکوس باید بلافاصله منتقل شود به اولین عنصر تعاملی داخل مدال (معمولاً دکمه «بستن» یا «تأیید»). پس از بسته شدن — به عنصری که پنجره مدال را باز کرده بود برگردد. این الزام WCAG 2.4.3 و همزمان یک اشتباه رایج است.

iOS: مدیریت ترتیب فوکوس

در iOS، VoiceOver به‌طور خودکار ترتیب را بر اساس هندسه می‌سازد: عناصر ابتدا بر اساس Y و سپس بر اساس X مرتب می‌شوند. برای صفحه‌های با ساختار پیچیده این ترتیب ممکن است نادرست باشد — توسعه‌دهنده باید مداخله کند.

ابزارهای اصلی:

  • shouldGroupAccessibilityElement — عناصر فرزند را در یک بلوک منطقی واحد ترکیب می‌کند
  • accessibilityElements — آرایه‌ای که ترتیب سفارشی عناصر فرزند را تعیین می‌کند
  • UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, element) — جابه‌جایی برنامه‌نویسی‌شده فوکوس

نمونه تنظیم ترتیب سفارشی برای کارت محصول:

swift
class ProductCardView: UIView {
    let titleLabel = UILabel()
    let priceLabel = UILabel()
    let buyButton = UIButton()

    override var accessibilityElements: [Any]? {
        get {
            return [titleLabel!, priceLabel!, buyButton!]
        }
        set {}
    }
}

برای جابه‌جایی برنامه‌نویسی‌شده فوکوس پس از یک عمل:

swift
UIAccessibility.post(
    notification: .layoutChanged,
    argument: newlyAddedItem
)

shouldGroupAccessibilityElement در عمل

ویژگی shouldGroupAccessibilityElement برای کارت‌های موجود در مجموعه‌ها مفید است. اگر روی کارت والد true تنظیم شود، VoiceOver کل کارت را به‌عنوان یک عنصر واحد درک می‌کند. کاربر می‌تواند دو بار لمس کند تا کل کارت را فعال کند یا چرخشگر را برای پیمایش داخل آن تنظیم کند. برای UICollectionViewCell و UITableViewCell توصیه می‌شود.

Android: ویژگی‌های جهت فوکوس

در Android، TalkBack نیز از ترتیب هندسی استفاده می‌کند، اما اولویت به ویژگی‌های صریح nextFocus* داده می‌شود. این ویژگی‌ها در XML یا به‌صورت برنامه‌نویسی تعیین می‌شوند:

ویژگیکاربردنمونه
nextFocusDownعنصر هنگام پیمایش به پایین@+id/field_email
nextFocusUpعنصر هنگام پیمایش به بالا@+id/field_name
nextFocusLeftعنصر در سمت چپ@+id/btn_back
nextFocusRightعنصر در سمت راست@+id/btn_next

نمونه برای فرم ثبت‌نام:

xml
<EditText
    android:id="@+id/field_email"
    android:nextFocusDown="@+id/field_password" />

<EditText
    android:id="@+id/field_password"
    android:nextFocusDown="@+id/btn_submit" />

برای RecyclerView ترتیب فوکوس پویا است — توسط آداپتر تعیین می‌شود. اگر سلول‌ها ساختار پیچیده‌ای دارند، descendantFocusability = "beforeDescendants" را تنظیم کنید و ترتیب را در گره عنصر فهرست تعیین کنید. برای Jetpack Compose ترتیب فوکوس از طریق Modifier.focusOrder() و FocusOrder تنظیم می‌شود. اولویت: previous (فرزند)، next (بعدی)، custom key.

TouchDelegate، ناحیه لمس و محدوده فوکوس

اگر عنصر برای فوکوس خیلی کوچک است (کمتر از 44pt)، ناحیه لمس را از طریق TouchDelegate در iOS یا minWidth/minHeight در Android افزایش دهید. طبق Google Material Design, 2024، حداقل ناحیه لمس 48×48dp است. VoiceOver و TalkBack روی bounding box عنصر فوکوس می‌کنند. عناصر کوچک‌تر از 30pt ممکن است برای فوکوس حرکتی در دسترس نباشند — کاربر فیزیکی نمی‌تواند با انگشت به آن‌ها برسد.

نقض‌های رایج WCAG 2.4.3

فوکوس پرشی — زمانی که پس از یک عمل (مثلاً حذف یک عنصر) فوکوس به ابتدای فهرست یا دکمه سیستمی «بازگشت» منتقل می‌شود. کاربر VoiceOver زمینه را از دست می‌دهد. راه‌حل: فوکوس را به‌صورت برنامه‌نویسی به عنصری که نزدیک به عنصر حذف‌شده است منتقل کنید.

فوکوس نامرئی — عنصر فوکوس دریافت می‌کند، اما نشانگر بصری وجود ندارد (کاربران صفحه‌کلید نمی‌بینند کجا هستند). در iOS برای نشانگرهای سفارشی UIAccessibility.isVoiceOverRunning را بررسی کنید. طبق Deque University, 2024، فوکوس نامرئی دومین دلیل رایج شکست ممیزی دسترس‌پذیری است.

پنجره‌های مدال — فوکوس پس از باز شدن پنجره مدال روی محتوای پس‌زمینه باقی می‌ماند. در iOS، نمای مدال به‌طور خودکار فوکوس را جذب می‌کند اگر modalPresentationStyle = .pageSheet تنظیم شده باشد. در Android از setFocusable(true) روی کانتینر دیالوگ استفاده کنید.

Focus trap (تله فوکوس)

مشکل معکوس: فوکوس داخل پنجره مدال گیر می‌کند و نمی‌تواند خارج شود (به‌جز با بستن). این فقط برای پنجره‌های مدال مجاز است — کاربر باید آگاهانه پنجره را ببندد. برای صفحه‌های عادی تله فوکوس یک خطای بحرانی است. راه‌حل: مطمئن شوید که آخرین عنصر پنجره مدال (دکمه «بستن») فوکوس را به عقب برمی‌گرداند.

صفحه‌های سفارشی و فوکوس برنامه‌نویسی‌شده

برای صفحه‌های سفارشی (نقشه‌ها، بوم‌ها، بازی‌ها) ترتیب هندسی خودکار قابل استفاده نیست. توسعه‌دهنده باید درخت دسترس‌پذیری را به‌صورت دستی بسازد. در iOS برای این کار متد UIAccessibilityContainer بازنویسی می‌شود.

نمونه برای بوم سفارشی:

swift
class CanvasView: UIView {
    var shapes: [ShapeView] = []

    override var accessibilityElements: [Any]? {
        get {
            // شکل‌ها را بر اساس Z-index مرتب می‌کنیم، نه بر اساس هندسه
            return shapes.sorted { $0.zIndex < $1.zIndex }
        }
        set {}
    }
}

در Android برای View سفارشی onInitializeAccessibilityNodeInfo را بازنویسی کنید:

kotlin
override fun onInitializeAccessibilityNodeInfo(
    info: AccessibilityNodeInfo
) {
    super.onInitializeAccessibilityNodeInfo(info)
    info.addChild(firstElement)
    info.addChild(secondElement)
    info.isFocusable = true
}

برای فهرست‌های پویا (چت، فید خبری) پس از افزودن عنصر، فوکوس را به اولین عنصر جدید منتقل کنید. در iOS: UIAccessibility.post(notification: .layoutChanged, argument: newMessage). در Android: sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED).

AccessibilityFrame و هندسه فوکوس

iOS به‌طور خودکار محدوده فوکوس را بر اساس frame عنصر تعیین می‌کند. اگر عنصر تبدیل (transform, rotation) داشته باشد، VoiceOver ممکن است روی محدوده نادرست فوکوس کند. به‌صورت صریح accessibilityFrame را در مختصات صفحه تعیین کنید: element.accessibilityFrame = UIAccessibility.convertToScreenCoordinates(element.bounds, in: element). این تضمین می‌کند که VoiceOver ناحیه صحیح را برجسته کند.

UIKit Dynamics و دسترس‌پذیری

برای صفحه‌های انیمیشنی (UIKit Dynamics, Lottie, SpriteKit) فوکوس برنامه‌نویسی‌شده به‌ویژه مهم است. VoiceOver نمی‌تواند درخت دسترس‌پذیری را برای عناصر متحرک بسازد. isAccessibilityElement = false را روی کانتینرهای انیمیشن و true را فقط روی عناصر تعاملی داخل آن‌ها تنظیم کنید.

آزمایش ترتیب فوکوس

آزمایش دستی: VoiceOver (iOS) یا TalkBack (Android) را فعال کنید و با حرکت به راست کل ترتیب را طی کنید. فوکوس باید از ترتیب بصری پیروی کند — از چپ به راست، از بالا به پایین. هر عنصر تعاملی باید دقیقاً یک بار فوکوس دریافت کند.

آزمایش خودکار دشوار است اما ممکن:

swift
func testKeyboardFocusOrder() {
    let app = XCUIApplication()
    app.launch()
    app.textFields["Email"].tap()
    // Tab — فقط با صفحه‌کلید سخت‌افزاری
}

برای Android از Accessibility Testing Framework استفاده کنید:

kotlin
@Test
fun testFocusOrder() {
    onView(withId(R.id.fieldEmail))
        .check(matches(isFocusable()))
    onView(withId(R.id.fieldEmail))
        .perform(focus())
    onView(withId(R.id.fieldPassword))
        .check(matches(isFocused()))
}

مطمئن‌ترین روش — تست UI سناریو: فرم را قدم به قدم پر کنید (Email → رمز عبور → ارسال) و بررسی کنید که هر قدم با موفقیت تکمیل می‌شود. اگر ترتیب فوکوس نقض شده باشد، سناریو هنگام تلاش برای تعامل با عنصری خارج از فوکوس شکست می‌خورد.

Xcode Accessibility Inspector برای اشکال‌زدایی

ابزار Accessibility Inspector در Xcode درخت کامل دسترس‌پذیری را نشان می‌دهد. می‌توانید در ترتیب VoiceOver بین عناصر حرکت کنید و مسیر دقیق فوکوس را ببینید. از تب «Audit» برای جستجوی خودکار نقض‌های Focus Order استفاده کنید.

سوالات متداول

WCAG 2.4.3 چیست و چه الزاماتی برای فوکوس دارد؟

WCAG 2.4.3 (Focus Order) — معیار موفقیت سطح A. الزام می‌کند که ترتیب فوکوس معنای محتوا را هنگام پیمایش متوالی حفظ کند. نقض بحرانی تلقی می‌شود و گواهینامه را مسدود می‌کند.

چگونه ترتیب فوکوس را برای عناصر پنهان پشت انیمیشن تنظیم کنیم؟

عناصر پنهان باید در iOS isAccessibilityElement = false یا در Android visibility = gone/invisible داشته باشند. هنگام ظاهر شدن، فوکوس را از طریق UIAccessibility.post(notification: .layoutChanged) به‌صورت برنامه‌نویسی منتقل کنید.

تفاوت فوکوس در iOS و Android چیست؟

iOS از طریق accessibilityElements و shouldGroupAccessibilityElement مدیریت می‌کند، Android — از طریق ویژگی‌های nextFocus* و AccessibilityNodeInfo. اصل یکسان است: ترتیب هندسی به‌صورت پیش‌فرض با امکان بازنویسی.

اگر RecyclerView ترتیب نادرستی دارد چه باید کرد؟

descendantFocusability = "beforeDescendants" را روی عنصر ریشه تنظیم کنید و ترتیب را در آداپتر از طریق onInitializeAccessibilityNodeInfo برای هر سلول پیکربندی کنید.

چگونه فوکوس را بدون VoiceOver بررسی کنیم؟

صفحه‌کلید سخت‌افزاری را از طریق Bluetooth یا USB متصل کنید. در iOS برای جابه‌جایی فوکوس Tab را فشار دهید. در Android TalkBack را فعال کنید و از کلیدهای Tab و فلش استفاده کنید.

جمع‌بندی

  • Focus Order — ترتیب پیمایش عناصر هنگام پیمایش با صفحه‌کلید یا screen reader؛ مبتنی بر WCAG 2.4.3
  • فوکوس باید از ترتیب بصری (از چپ به راست، از بالا به پایین) پیروی کند — به‌طور خودکار در VoiceOver و TalkBack
  • در iOS ترتیب از طریق accessibilityElements و shouldGroupAccessibilityElement تنظیم می‌شود
  • در Android از ویژگی‌های nextFocusDown، nextFocusUp، nextFocusLeft، nextFocusRight استفاده می‌شود
  • صفحه‌های سفارشی (نقشه‌ها، بوم‌ها) نیاز به مدیریت برنامه‌نویسی‌شده فوکوس از طریق UIAccessibilityPostNotification دارند
  • نقض ترتیب — خطای بحرانی WCAG 2.4.3؛ کاربران زمینه را از دست می‌دهند و نمی‌توانند سناریو را کامل کنند
  • فوکوس را از طریق حرکات VoiceOver/TalkBack، صفحه‌کلید سخت‌افزاری و سناریوهای خودکار آزمایش کنید

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید