Adaptive Layout: кључни појмови, класе величина и квалификатори

Аутор: IT Sectr Објављено: 2026-02-26 Време читања: 10 мин

Adaptive Layout — приступ изради интерфејса при ком апликација користи различите макете за различите типове уређаја и оријентације екрана. За разлику од responsive design-а, adaptive layout се пребацује између унапред припремљених layouts у 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 користи дискретна пребацивања: телефон — један layout, таблет — други, desktop — трећи.

Apple је увео Size Classes у iOS 8 (2014) заједно са универзалним апликацијама (universal binaries, које раде и на iPhone-у и на iPad-у). Google је представио smallestWidth (swdp) квалификатор у Android 3.2 (2011). Оба механизма решавају исти задатак: дати програмеру алат за креирање различитих UI-јева за различите екране без дуплирања кода.

Модерне препоруке Apple HIG и Google Material Design се слажу да је adaptive layout обавезан за апликације које подржавају више од једне величине екрана. Google Play означава недостатак адаптације за таблете као недостатак. App Store нема формални захтев, али корисници iPad-а очекују native искуство, а не развучену iPhone апликацију.

iOS Size Classes и UITraitCollection

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.

Пример адаптивног layout-а у Swift-у

Адаптација кроз UITraitCollection у UIKit-у се реализује претплатом на промене trait collection-а и преправљањем layout-а. Пример испод показује контролер који се пребацује између вертикалног стека за 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/.

Пример адаптивног 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 пружа уграђене алате за адаптивни layout без директног коришћења 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 { // Приоритет 1: хоризонтални
                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 одређује величину прозора у runtime-у и реактивно прекомпонује 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 при расклапању. Препоручује се архитектура заснована на канонским layout-има (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 креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође