Adaptive Layout — interfeyslarni yig'ishga yondashuv bo'lib, unda dastur turli qurilma turlari va ekran orientatsiyalari uchun turli maketlardan foydalanadi. Responsive design-dan farqli o'laroq, adaptive layout breakpoint nuqtalarida oldindan tayyorlangan layoutlar o'rtasida almashadi. Maqola iOS-da Size Classes va UITraitCollection, Android-da sw600dp/layout-w600dp kvalifikatorlarini va amaliy misollarni ko'rib chiqadi.
Asosiy fikrlar
Adaptive Layout — interfeys qurish strategiyasi bo'lib, unda dasturchi har bir qurilma turi uchun alohida maketlar yaratadi va ekran xususiyatlariga qarab ular o'rtasida almashadi. Responsive design-dan farqli o'laroq, unda elementlar silliq oqadi, adaptive layout diskret almashinuvlardan foydalanadi: telefon — bitta layout, planshet — boshqa, desktop — uchinchi.
Apple Size Classes-ni iOS 8 da (2014) universal dasturlar (iPhone va iPad da ishlaydigan universal binaries) bilan birga taqdim etdi. Google smallestWidth (sw
Zamonaviy Apple HIG va Google Material Design tavsiyalari bir nechta ekran o'lchamini qo'llab-quvvatlaydigan dasturlar uchun adaptive layout majburiy ekanligiga qo'shiladi. Google Play planshetlarga moslashishning yo'qligini kamchilik sifatida belgilaydi. App Store rasmiy talabga ega emas, lekin iPad foydalanuvchilari cho'zilgan iPhone dasturini emas, balki native tajribani kutishadi.
Size Classes — iOS-ning har bir o'qda mavjud fazoni Compact yoki Regular deb belgilaydigan xususiyati. UITraitCollection — view iyerarxiyasi orqali muhit xususiyatlarini uzatish uchun iOS tizimi: o'lcham, ekran masshtabi, force touch capability, rang gamuti (light/dark), accessibility settings.
| Qurilma / Orientatsiya | Horizontal | Vertical |
|---|---|---|
| iPhone portret (barcha modellar) | Compact | Regular |
| iPhone Plus/Pro Max landshaft | Regular | Compact |
| iPhone SE / mini landshaft | Compact | Compact |
| iPad portret | Regular | Regular |
| iPad landshaft | Regular | Regular |
| iPad Split View (1/3 ekran) | Compact | Regular |
UITraitCollection tizim tomonidan yaratiladi va UIApplication dan UIWindow orqali har bir UIView ga uzatiladi. Orientatsiya yoki oyna o'lchami o'zgarganda (Split View) tizim yangi UITraitCollection yaratadi va traitCollectionDidChange ni chaqiradi. SwiftUI da o'zgarishlar Environment Values orqali kuzatiladi va view avtomatik qayta chiziladi.
UIKit da UITraitCollection orqali moslashish trait collection o'zgarishlariga obuna bo'lish va layout-ni qayta qurish bilan amalga oshiriladi. Quyidagi misol iPhone uchun vertikal stack va iPad uchun split-maket o'rtasida almashadigan kontrollerni ko'rsatadi.
// Size Classes bilan adaptiv kontroller
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: tab bar + 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 metodi traits dagi har qanday o'zgarishda chaqiriladi, lekin biz faqat horizontalSizeClass ni tekshiramiz — maket o'zgarishi uchun eng muhim ko'rsatkich. iOS 17+ da o'zgarishlarga obuna bo'lmasdan reaktiv yangilash uchun UIViewController.horizontalSizeClass ni computed property sifatida ishlatish tavsiya etiladi.
Android ikkita asosiy en kvalifikatorini taqdim etadi: sw
| Kvalifikator | Shart | Qurilma namunasi |
|---|---|---|
| layout-sw600dp | Minimal en ≥ 600 dp | iPad (768 dp), Pixel C (900 dp) |
| layout-w600dp | Joriy en ≥ 600 dp | iPad Split View da (600 dp), landshaftda telefon |
| layout-land | Landshaft orientatsiyasi | Gorizontal aylantirilgan har qanday qurilma |
| layout-sw720dp | Minimal en ≥ 720 dp | iPad Pro (833 dp), Galaxy Tab S9 (800 dp) |
Android da resurs iyerarxiyasi: res/layout/activity_main.xml (telefon), res/layout-sw600dp/activity_main.xml (7" planshet), res/layout-w600dp-land/activity_main.xml (landshaftda telefon). Android joriy qurilma konfiguratsiyasiga mos keladigan eng spesifik kvalifikatorni tanlaydi. Mos fayl bo'lmasa, res/layout/ dan asosiy resurs ishlatiladi.
Zamonaviy Android rivojlanishi resurslarni to'g'ridan-to'g'ri o'qish o'rniga oyna o'lchamini aniqlash uchun Jetpack WindowManager dan foydalanadi. Bu ekran o'lchami dinamik o'zgaradigan yig'iladigan qurilmalar uchun ayniqsa muhimdir (Galaxy Fold 6.2" dan 7.6" gacha ochiladi).
// Jetpack WindowManager orqali moslashish
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("Telefon", 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()
}
}androidx.window kutubxonasidan currentWindowAdaptiveInfo() komponenti interfeysni istalgan qurilmaga moslashtiradi: telefon, planshet, yig'iladigan, ChromeOS. En sinflari: COMPACT (0–600 dp), MEDIUM (600–840 dp), EXPANDED (>840 dp). Balandlik sinflari: COMPACT (0–480 dp), MEDIUM (480–900 dp), EXPANDED (>900 dp).
SwiftUI Size Classes dan to'g'ridan-to'g'ri foydalanmasdan adaptiv layout uchun o'rnatilgan vositalarni taqdim etadi. AnyLayout, ViewThatFits va GeometryReader mavjud bo'shliqqa avtomatik moslashadigan interfeyslarni qurishga imkon beradi.
// AnyLayout va ViewThatFits orqali SwiftUI moslashuvi
import SwiftUI
struct AdaptiveGrid: View {
@Environment(\.horizontalSizeClass) private var hSizeClass
var body: some View {
let layout = hSizeClass == .regular
? AnyLayout(HStackLayout())
: AnyLayout(VStackLayout())
layout {
Label("Sevimlilar", systemImage: "star")
Label("Oxirgi", systemImage: "clock")
Label("Sozlamalar", systemImage: "gear")
}
.padding()
}
}
// ViewThatFits — layout ni avtomatik tanlash
struct SmartLayout: View {
var body: some View {
ViewThatFits {
HStack { // Prioritet 1: gorizontal
CardView()
CardView()
}
VStack { // Sig'masa — vertikal
CardView()
CardView()
}
}
}
}ViewThatFits — SwiftUI ning kuchli vositasi, kesilmasdan mavjud bo'shliqqa sig'adigan birinchi bola view-ni avtomatik tanlaydi. Bu oddiy adaptiv almashinuvlar uchun Size Classes ni aniq tekshirish zaruratini yo'q qiladi. AnyLayout view tanasida if-else holda layout turini (HStack/VStack) o'zgartirishga imkon beradi.
Jetpack Compose adaptiv maket uchun WindowSizeClass (Material 3 adaptive kutubxonasidan) va BoxWithConstraints dan foydalanadi. XML kvalifikatorlaridan farqli o'laroq, Compose oyna o'lchamini runtime da aniqlaydi va konfiguratsiya o'zgarganda UI ni reaktiv qayta quradi.
// BoxWithConstraints orqali Jetpack Compose moslashuvi
@Composable
fun AdaptiveList(items: List<String>) {
BoxWithConstraints {
val width = maxWidth
if (width >= 600.dp) {
// Planshet: ikki ustun (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 {
// Telefon: navigatsiya bilan bir ustun
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 tayyor komponentlarni taqdim etadi: ListDetailScaffold, SupportingPaneScaffold, NavigationSuiteScaffold. Bu komponentlar avtomatik ravishda WindowWidthSizeClass ga moslashadi — stacked (COMPACT) va side-by-side (MEDIUM/EXPANDED) o'rtasida almashadi. Dasturchi faqat namunani tanlashi va kontent panellarini uzatishi kerak.
Tez-tez beriladigan savollar
sw600dp (smallestWidth) — orientatsiyadan qat'i nazar minimal ekran eni. Har doim 7"+ planshetlarda ishlaydi. w600dp (available width) — joriy orientatsiyani hisobga olgan holda mavjud en. Eni 600 dp dan oshishi mumkin bo'lgan landshaftdagi telefonlarda ishlaydi.
UITraitCollection — muhit xususiyatlarini o'z ichiga olgan iOS obyekti: size classes (horizontal/vertical), display scale, force touch capability, user interface idiom (iPhone/iPad), color gamut, accessibility settings. UIApplication dan view iyerarxiyasi orqali uzatiladi va orientatsiya yoki oyna o'lchami o'zgarganda yangilanadi.
Jetpack WindowManager (Android) va UIScreen nativeBounds + traitCollection (iOS) dan foydalaning. Yig'iladigan qurilmalar ochilganda Size Class ni o'zgartiradi. Activity ni qayta ishga tushirmasdan oyna o'lchami o'zgarishlariga reaktiv obuna bilan kanonik layout (list-detail, supporting pane) asosidagi arxitektura tavsiya etiladi.
Yo'q. Size Classes variatsiyalari bo'lgan bitta storyboard yoki UIKit orqali programm layout dan foydalaning. SwiftUI umuman storyboards talab qilmaydi. iPad uchun alohida storyboards takrorlash yaratadi va texnik xizmat ko'rsatishni murakkablashtiradi — bitta maketni Size Classes orqali moslashtirish yaxshiroq.
Canonical layouts — Google dan tayyor adaptiv maket namunalari: list-detail (ro'yxat + tafsilot), supporting pane (kontent + harakat paneli), feed (kartalar). Har bir namuna uch holatni qo'llab-quvvatlaydi (compact/medium/expanded) va Material 3 Adaptive kutubxonasida ListDetailScaffold va SupportingPaneScaffold orqali amalga oshirilgan.
Xulosa
Biz kalit topshirig'i bilan mobil ilovani ishlab chiqamiz
IT Sectr 2017-yildan beri startaplar va korxonalar uchun iOS va Android ilovalarini yaratadi. Biz sizga maslahat beramiz va eng yaxshi yechimni taklif qilamiz.