Adaptive Layout: مفاهیم کلیدی، کلاس‌های اندازه و واجدشرایط‌ها

نویسنده: IT Sectr منتشر شده: 2026-02-26 زمان مطالعه: 10 دقیقه

Adaptive Layout — رویکردی به چیدمان واسط‌ها که در آن برنامه از طرح‌بندی‌های مختلف برای انواع دستگاه‌ها و جهت‌گیری‌های صفحه استفاده می‌کند. بر خلاف responsive design، adaptive layout بین طرح‌بندی‌های از پیش آماده شده در نقاط breakpoint جابجا می‌شود. مقاله به بررسی Size Classes و UITraitCollection در iOS، واجدشرایط‌های sw600dp/layout-w600dp در Android و ارائه مثال‌های عملی می‌پردازد.

نکات اصلی

  • Adaptive Layout — جابجایی گسسته بین طرح‌بندی‌ها بسته به اندازه صفحه، جهت‌گیری و نوع دستگاه
  • Size Classes در iOS فضا را به صورت Compact یا Regular در افقی و عمودی طبقه‌بندی می‌کند
  • UITraitCollection — ظرف ویژگی‌های محیط iOS (اندازه، مقیاس، force touch، طیف رنگ)
  • sw600dp — واجدشرایط حداقل عرض Android برای تبلت‌ها (7" و بیشتر)
  • layout-w600dp — واجدشرایط عرض قابل دسترس که با فضای کافی در جهت‌گیری فعلی فعال می‌شود

Adaptive Layout چیست؟

Adaptive Layout — استراتژی ساخت واسط که در آن توسعه‌دهنده طرح‌بندی‌های جداگانه‌ای برای هر نوع دستگاه ایجاد می‌کند و بر اساس ویژگی‌های صفحه بین آنها جابجا می‌شود. بر خلاف responsive design که عناصر به آرامی جریان می‌یابند، adaptive layout از جابجایی‌های گسسته استفاده می‌کند: تلفن — یک طرح، تبلت — طرح دیگر، دسکتاپ — طرح سوم.

Apple Size Classes را در iOS 8 (2014) همراه با برنامه‌های universal (universal binaries که هم در iPhone و هم در iPad کار می‌کنند) معرفی کرد. Google واجدشرایط smallestWidth (swdp) را در Android 3.2 (2011) معرفی کرد. هر دو مکانیزم یک مسئله را حل می‌کنند: به توسعه‌دهنده ابزاری برای ایجاد UI متفاوت برای صفحه‌های مختلف بدهند بدون تکرار کد.

توصیه‌های مدرن Apple HIG و Google Material Design بر این توافق دارند که adaptive layout برای برنامه‌هایی که بیش از یک اندازه صفحه را پشتیبانی می‌کنند الزامی است. Google Play عدم تطبیق با تبلت‌ها را به عنوان نقص علامت‌گذاری می‌کند. App Store الزام رسمی ندارد، اما کاربران iPad انتظار تجربه بومی را دارند، نه برنامه کشیده شده iPhone.

Size Classes و UITraitCollection در iOS

Size Classes — ویژگی iOS که فضای موجود را در هر محور به صورت Compact یا Regular تعیین می‌کند. UITraitCollection — سیستم iOS برای انتقال ویژگی‌های محیط از طریق سلسله‌مراتب view: اندازه، مقیاس صفحه، force touch capability، طیف رنگ (light/dark)، accessibility settings.

دستگاه / جهت‌گیریHorizontalVertical
iPhone عمودی (همه مدل‌ها)CompactRegular
iPhone Plus/Pro Max افقیRegularCompact
iPhone SE / mini افقیCompactCompact
iPad عمودیRegularRegular
iPad افقیRegularRegular
iPad Split View (1/3 صفحه)CompactRegular

UITraitCollection توسط سیستم ساخته می‌شود و از UIApplication از طریق UIWindow به هر UIView منتقل می‌شود. هنگام تغییر جهت‌گیری یا اندازه پنجره (Split View) سیستم یک UITraitCollection جدید تولید می‌کند و traitCollectionDidChange را فراخوانی می‌کند. در SwiftUI تغییرات از طریق Environment Values ردیابی می‌شوند و به طور خودکار view را دوباره ترسیم می‌کنند.

مثال چیدمان تطبیقی در Swift

تطبیق از طریق UITraitCollection در UIKit با اشتراک در تغییرات trait collection و بازسازی layout پیاده‌سازی می‌شود. مثال زیر یک کنترلر را نشان می‌دهد که بین stack عمودی برای iPhone و طرح split برای iPad جابجا می‌شود.

swift
// کنترلر تطبیقی با Size Classes
import UIKit

final class AdaptiveViewController: UIViewController {

    private let compactStack = UIStackView()
    private let regularStack = UIStackView()
    private let sidebar = UIView()
    private let content = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        setupViews()
        updateLayout(for: traitCollection)
    }

    override func traitCollectionDidChange(
        _ previousTraitCollection: UITraitCollection?
    ) {
        super.traitCollectionDidChange(previousTraitCollection)
        if traitCollection.horizontalSizeClass
            != previousTraitCollection?.horizontalSizeClass {
            updateLayout(for: traitCollection)
        }
    }

    private func updateLayout(for traits: UITraitCollection) {
        if traits.horizontalSizeClass == .regular {
            showRegularLayout() // iPad: sidebar + content
        } else {
            showCompactLayout() // iPhone: نوار برگه + push
        }
    }

    private func showRegularLayout() {
        view.subviews.forEach { $0.removeFromSuperview() }
        regularStack.addArrangedSubview(sidebar)
        regularStack.addArrangedSubview(content)
        regularStack.frame = view.bounds
        regularStack.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(regularStack)
    }

    private func showCompactLayout() {
        view.subviews.forEach { $0.removeFromSuperview() }
        compactStack.addArrangedSubview(content)
        compactStack.frame = view.bounds
        compactStack.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        view.addSubview(compactStack)
    }

    private func setupViews() {
        sidebar.backgroundColor = .systemGray6
        content.backgroundColor = .systemBackground
        compactStack.axis = .vertical
        regularStack.axis = .horizontal
    }
}

متود traitCollectionDidChange در هر تغییری در traits فراخوانی می‌شود، اما ما فقط horizontalSizeClass را بررسی می‌کنیم — مهم‌ترین نشانگر برای تغییر طرح. در iOS 17+ توصیه می‌شود از UIViewController.horizontalSizeClass به عنوان computed property برای به‌روزرسانی واکنشی بدون اشتراک در تغییرات استفاده کنید.

واجدشرایط‌های Android: sw600dp و w600dp

Android دو واجدشرایط اصلی عرض ارائه می‌دهد: swdp (smallestWidth) و wdp (available width). تفاوت حیاتی است: sw — حداقل عرض صفحه بدون در نظر گرفتن جهت‌گیری، w — عرض قابل دسترس با در نظر گرفتن جهت‌گیری فعلی. sw تضمین می‌کند که layout برای دستگاهی که از نظر فیزیکی حداقل عرض مشخص شده را دارد بارگذاری شود. w به چرخش واکنش نشان می‌دهد — در حالت افقی ممکن است w600dp فعال شود، حتی اگر در حالت عمودی عرض 360 dp باشد.

واجدشرایطشرطنمونه دستگاه
layout-sw600dpحداقل عرض ≥ 600 dpiPad (768 dp), Pixel C (900 dp)
layout-w600dpعرض فعلی ≥ 600 dpiPad در Split View (600 dp), تلفن در حالت افقی
layout-landجهت‌گیری افقیهر دستگاهی که به صورت افقی چرخانده شده
layout-sw720dpحداقل عرض ≥ 720 dpiPad Pro (833 dp), Galaxy Tab S9 (800 dp)

سلسله‌مراتب منابع در Android: res/layout/activity_main.xml (تلفن), res/layout-sw600dp/activity_main.xml (تبلت 7"), res/layout-w600dp-land/activity_main.xml (تلفن در حالت افقی). Android خاص‌ترین واجدشرایط مطابق با پیکربندی فعلی دستگاه را انتخاب می‌کند. در صورت نبود فایل مناسب، از منبع پایه در res/layout/ استفاده می‌شود.

مثال چیدمان تطبیقی در Kotlin

توسعه مدرن Android از Jetpack WindowManager برای تعیین اندازه پنجره به جای خواندن مستقیم منابع استفاده می‌کند. این به ویژه برای دستگاه‌های تاشو که اندازه صفحه می‌تواند به صورت پویا تغییر کند (Galaxy Fold از 6.2" به 7.6" باز می‌شود) مهم است.

kotlin
// تطبیق از طریق Jetpack WindowManager
import androidx.window.core.layout.WindowHeightSizeClass
import androidx.window.core.layout.WindowSizeClass
import androidx.window.core.layout.WindowWidthSizeClass

@OptIn(ExperimentalLayoutApi::class)
@Composable
fun AdaptiveScreen() {
    val windowSizeClass = currentWindowAdaptiveInfo().windowSizeClass

    FlowRow(
        verticalAlignment = Arrangement.Top,
        horizontalArrangement = when (windowSizeClass.windowWidthSizeClass) {
            WindowWidthSizeClass.EXPANDED -> Arrangement.SpaceEvenly
            else -> Arrangement.Start
        }
    ) {
        when (windowSizeClass.windowWidthSizeClass) {
            WindowWidthSizeClass.COMPACT -> {
                CompactContent()
            }
            WindowWidthSizeClass.MEDIUM -> {
                MediumContent()
            }
            WindowWidthSizeClass.EXPANDED -> {
                ExpandedContent()
            }
        }
    }
}

@Composable
private fun CompactContent() {
    Column(modifier = Modifier.padding(16.dp)) {
        Text("تلفن", style = MaterialTheme.typography.headlineSmall)
        ListContent()
    }
}

@Composable
private fun MediumContent() {
    Row(modifier = Modifier.padding(24.dp)) {
        NavigationRail { ... }
        Column { ListContent() }
    }
}

@Composable
private fun ExpandedContent() {
    Row(modifier = Modifier.padding(32.dp)) {
        PermanentNavigationDrawer { ... }
        Column {
            ListContent()
        }
        DetailPane()
    }
}

کامپوننت currentWindowAdaptiveInfo() از کتابخانه androidx.window واسط را با هر دستگاهی تطبیق می‌دهد: تلفن، تبلت، تاشو، ChromeOS. کلاس‌های عرض: COMPACT (0–600 dp), MEDIUM (600–840 dp), EXPANDED (>840 dp). کلاس‌های ارتفاع: COMPACT (0–480 dp), MEDIUM (480–900 dp), EXPANDED (>900 dp).

Adaptive Layout در SwiftUI

SwiftUI ابزارهای داخلی برای چیدمان تطبیقی بدون استفاده مستقیم از Size Classes فراهم می‌کند. AnyLayout, ViewThatFits و GeometryReader امکان ساخت واسط‌هایی را می‌دهند که به طور خودکار با فضای موجود تطبیق می‌یابند.

swift
// تطبیق SwiftUI از طریق AnyLayout و ViewThatFits
import SwiftUI

struct AdaptiveGrid: View {
    @Environment(\.horizontalSizeClass) private var hSizeClass

    var body: some View {
        let layout = hSizeClass == .regular
            ? AnyLayout(HStackLayout())
            : AnyLayout(VStackLayout())

        layout {
            Label("مورد علاقه", systemImage: "star")
            Label("اخیر", systemImage: "clock")
            Label("تنظیمات", systemImage: "gear")
        }
        .padding()
    }
}

// ViewThatFits — انتخاب خودکار layout
struct SmartLayout: View {
    var body: some View {
        ViewThatFits {
            HStack { // اولویت ۱: افقی
                CardView()
                CardView()
            }
            VStack { // اگر جا نشد — عمودی
                CardView()
                CardView()
            }
        }
    }
}

ViewThatFits — ابزار قدرتمند SwiftUI که به طور خودکار اولین view فرزندی را که بدون برش در فضای موجود قرار می‌گیرد انتخاب می‌کند. این کار نیاز به بررسی صریح Size Classes برای جابجایی‌های تطبیقی ساده را از بین می‌برد. AnyLayout امکان تغییر نوع layout (HStack/VStack) را بدون if-else در بدنه view فراهم می‌کند.

Adaptive Layout در Jetpack Compose

Jetpack Compose از WindowSizeClass (از کتابخانه Material 3 adaptive) و BoxWithConstraints برای چیدمان تطبیقی استفاده می‌کند. بر خلاف واجدشرایط XML، Compose اندازه پنجره را در زمان اجرا تعیین می‌کند و UI را هنگام تغییر پیکربندی به صورت واکنشی بازسازی می‌کند.

kotlin
// تطبیق Jetpack Compose از طریق BoxWithConstraints
@Composable
fun AdaptiveList(items: List<String>) {
    BoxWithConstraints {
        val width = maxWidth

        if (width >= 600.dp) {
            // تبلت: دو ستون (list-detail)
            Row(modifier = Modifier.fillMaxSize()) {
                LazyColumn(modifier = Modifier.weight(1f)) {
                    items(items) { item ->
                        ListItem(text = item)
                    }
                }
                var selected by remember { mutableStateOf(items.first()) }
                DetailPanel(item = selected)
                    .weight(2f)
            }
        } else {
            // تلفن: یک ستون با ناوبری
            LazyColumn(modifier = Modifier.fillMaxSize()) {
                items(items) { item ->
                    ListItem(text = item, onClick = { navigateToDetail(item) })
                }
            }
        }
    }
}

@Composable
fun AdaptiveListMaterial3() {
    val windowClass = currentWindowAdaptiveInfo().windowSizeClass

    AdaptiveLayout(
        layout = windowClass.windowWidthSizeClass
    ) {
        when (windowClass.windowWidthSizeClass) {
            WindowWidthSizeClass.COMPACT -> {
                ListDetailScaffold(
                    isDetailOnly = false,
                    list = { ListPane() },
                    detail = { DetailPane() }
                )
            }
            else -> {
                ListDetailScaffold(
                    isDetailOnly = false,
                    list = { ListPane() },
                    detail = { DetailPane() }
                )
            }
        }
    }
}

Material 3 Adaptive Layouts کامپوننت‌های آماده ارائه می‌دهد: ListDetailScaffold, SupportingPaneScaffold, NavigationSuiteScaffold. این کامپوننت‌ها به طور خودکار با WindowWidthSizeClass تطبیق می‌یابند — بین stacked (COMPACT) و side-by-side (MEDIUM/EXPANDED) جابجا می‌شوند. توسعه‌دهنده فقط باید الگو را انتخاب کند و پنل‌های محتوا را ارسال کند.

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

تفاوت بین sw600dp و w600dp چیست؟

sw600dp (smallestWidth) — حداقل عرض صفحه بدون در نظر گرفتن جهت‌گیری. همیشه در تبلت‌های 7"+ فعال است. w600dp (available width) — عرض قابل دسترس با در نظر گرفتن جهت‌گیری فعلی. در تلفن‌های در حالت افقی که عرض ممکن است بیش از 600 dp باشد فعال می‌شود.

UITraitCollection در iOS چیست؟

UITraitCollection — شیء iOS حاوی ویژگی‌های محیط: size classes (horizontal/vertical), display scale, force touch capability, user interface idiom (iPhone/iPad), color gamut, accessibility settings. از UIApplication از طریق سلسله‌مراتب view منتقل می‌شود و با تغییر جهت‌گیری یا اندازه پنجره به‌روز می‌شود.

چگونه برنامه را برای دستگاه‌های تاشو تطبیق دهیم؟

از Jetpack WindowManager (Android) و UIScreen nativeBounds + traitCollection (iOS) استفاده کنید. دستگاه‌های تاشو هنگام باز شدن Size Class را تغییر می‌دهند. معماری مبتنی بر طرح‌بندی‌های متعارف (list-detail, supporting pane) با اشتراک واکنشی در تغییرات اندازه پنجره بدون راه‌اندازی مجدد Activity توصیه می‌شود.

آیا نیاز به ایجاد storyboards جداگانه برای iPad است؟

خیر. از یک storyboard با تغییرات Size Classes constraint یا layout برنامه‌نویسی شده از طریق UIKit استفاده کنید. SwiftUI اصلاً به storyboards نیاز ندارد. storyboards جداگانه برای iPad باعث تکرار و پیچیدگی نگهداری می‌شود — بهتر است یک طرح را از طریق Size Classes تطبیق دهید.

canonical layouts در Material 3 چیست؟

Canonical layouts — الگوهای آماده چیدمان تطبیقی از Google: list-detail (لیست + جزئیات), supporting pane (محتوا + پنل اقدام), feed (کارت‌ها). هر الگو سه حالت را پشتیبانی می‌کند (compact/medium/expanded) و در کتابخانه Material 3 Adaptive از طریق ListDetailScaffold و SupportingPaneScaffold پیاده‌سازی شده است.

خلاصه

  • Adaptive Layout — جابجایی گسسته طرح‌بندی‌ها در نقاط breakpoint برای دستگاه‌های مختلف
  • Size Classes iOS (Compact/Regular) layout را برای iPhone, iPad و Split View تعیین می‌کنند
  • UITraitCollection — مکانیزم انتقال ویژگی‌های محیط از طریق سلسله‌مراتب view
  • sw600dp — واجدشرایط حداقل عرض Android برای تبلت‌های 7"+
  • w600dp — واجدشرایط عرض قابل دسترس که با چرخش به افقی فعال می‌شود
  • Jetpack WindowManager WindowSizeClass را فراهم می‌کند (WindowWidthSizeClass: COMPACT/MEDIUM/EXPANDED)
  • Material 3 Adaptive — کامپوننت‌های آماده با تطبیق خودکار (ListDetailScaffold)

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

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

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

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