Adaptive Layout: nyckelbegrepp, storleksklasser och kvalificerare

Författare: IT Sectr Publicerad: 2026-02-26 Lästid: 10 min

Adaptive Layout — ett tillvägagångssätt för att bygga gränssnitt där appen använder olika layouter för olika typer av enheter och skärmorienteringar. Till skillnad från responsive design växlar adaptive layout mellan förberedda layouter vid breakpoint-punkter. Artikeln behandlar Size Classes och UITraitCollection i iOS, kvalificerare sw600dp/layout-w600dp i Android och ger praktiska exempel.

Huvudpunkter

  • Adaptive Layout — diskret växling av layouter beroende på skärmstorlek, orientering och enhetstyp
  • Size Classes i iOS klassificerar utrymme som Compact eller Regular horisontellt och vertikalt
  • UITraitCollection — behållare för iOS-miljöegenskaper (storlek, skala, force touch, färgomfång)
  • sw600dp — Android-kvalificerare för minsta bredd för surfplattor (7" och större)
  • layout-w600dp — kvalificerare för tillgänglig bredd som aktiveras när det finns tillräckligt med utrymme i aktuell orientering

Vad är Adaptive Layout?

Adaptive Layout — en strategi för att bygga gränssnitt där utvecklaren skapar separata layouter för varje enhetstyp och växlar mellan dem baserat på skärmegenskaper. Till skillnad från responsive design, där element flyter smidigt, använder adaptive layout diskreta växlingar: telefon — en layout, surfplatta — en annan, desktop — en tredje.

Apple introducerade Size Classes i iOS 8 (2014) tillsammans med universella appar (universal binaries som fungerar på både iPhone och iPad). Google introducerade smallestWidth (swdp) kvalificeraren i Android 3.2 (2011). Båda mekanismerna löser samma problem: ge utvecklaren ett verktyg för att skapa olika UI för olika skärmar utan kodduplicering.

Moderna rekommendationer från Apple HIG och Google Material Design är överens om att adaptive layout är obligatoriskt för appar som stöder mer än en skärmstorlek. Google Play markerar bristen på anpassning för surfplattor som en brist. App Store har inget formellt krav, men iPad-användare förväntar sig en naturlig upplevelse, inte en utsträckt iPhone-app.

iOS Size Classes och UITraitCollection

Size Classes — iOS-egenskap som definierar tillgängligt utrymme som Compact eller Regular på varje axel. UITraitCollection — iOS-system för att överföra miljöegenskaper genom vyerarkin: storlek, skärm skala, force touch capability, färgomfång (light/dark), accessibility settings.

Enhet / OrienteringHorizontalVertical
iPhone porträtt (alla modeller)CompactRegular
iPhone Plus/Pro Max landskapRegularCompact
iPhone SE / mini landskapCompactCompact
iPad porträttRegularRegular
iPad landskapRegularRegular
iPad Split View (1/3 skärm)CompactRegular

UITraitCollection skapas av systemet och överförs från UIApplication via UIWindow till varje UIView. När orientering eller fönsterstorlek ändras (Split View) genererar systemet en ny UITraitCollection och anropar traitCollectionDidChange. I SwiftUI spåras ändringar via Environment Values och ritar automatiskt om vyn.

Exempel på adaptiv layout i Swift

Anpassning via UITraitCollection i UIKit implementeras genom att prenumerera på trait collection-ändringar och bygga om layouten. Exemplet nedan visar en kontrollenhet som växlar mellan en vertikal stack för iPhone och en split-layout för iPad.

swift
// Adaptiv kontrollenhet med 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: sidofält + innehåll
        } else {
            showCompactLayout() // iPhone: flikfält + 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
    }
}

Metoden traitCollectionDidChange anropas vid varje ändring av traits, men vi kontrollerar bara horizontalSizeClass — den mest betydelsefulla indikatorn för layoutändring. I iOS 17+ rekommenderas att använda UIViewController.horizontalSizeClass som computed property för reaktiv uppdatering utan att prenumerera på ändringar.

Android-kvalificerare: sw600dp och w600dp

Android tillhandahåller två huvudsakliga breddkvalificerare: swdp (smallestWidth) och wdp (available width). Skillnaden är kritisk: sw — minsta skärmbredd oavsett orientering, w — tillgänglig bredd med hänsyn till aktuell orientering. sw garanterar att layouten laddas för en enhet som fysiskt har den angivna minsta bredden. w reagerar på rotation — i landskapsläge kan w600dp aktiveras, även om bredden i porträtt är 360 dp.

KvalificerareVillkorEnhetsexempel
layout-sw600dpMinsta bredd ≥ 600 dpiPad (768 dp), Pixel C (900 dp)
layout-w600dpAktuell bredd ≥ 600 dpiPad i Split View (600 dp), telefon i landskap
layout-landLandskapsorienteringVilken enhet som helst roterad horisontellt
layout-sw720dpMinsta bredd ≥ 720 dpiPad Pro (833 dp), Galaxy Tab S9 (800 dp)

Resurshierarki i Android: res/layout/activity_main.xml (telefon), res/layout-sw600dp/activity_main.xml (7" surfplatta), res/layout-w600dp-land/activity_main.xml (telefon i landskap). Android väljer den mest specifika kvalificeraren som passar aktuell enhetskonfiguration. Om ingen lämplig fil finns används basresursen från res/layout/.

Exempel på adaptiv layout i Kotlin

Modern Android-utveckling använder Jetpack WindowManager för att bestämma fönsterstorlek istället för att direkt läsa resurser. Detta är särskilt viktigt för vikbara enheter, där skärmstorleken kan ändras dynamiskt (Galaxy Fold vecklas ut från 6,2" till 7,6").

kotlin
// Anpassning via 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("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()
    }
}

Komponenten currentWindowAdaptiveInfo() från biblioteket androidx.window anpassar gränssnittet till vilken enhet som helst: telefon, surfplatta, vikbar, ChromeOS. Breddklasser: COMPACT (0–600 dp), MEDIUM (600–840 dp), EXPANDED (>840 dp). Höjdklasser: COMPACT (0–480 dp), MEDIUM (480–900 dp), EXPANDED (>900 dp).

Adaptive Layout i SwiftUI

SwiftUI tillhandahåller inbyggda verktyg för adaptiv layout utan direkt användning av Size Classes. AnyLayout, ViewThatFits och GeometryReader gör det möjligt att bygga gränssnitt som automatiskt anpassar sig till tillgängligt utrymme.

swift
// SwiftUI-anpassning via AnyLayout och 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("Favoriter", systemImage: "star")
            Label("Senaste", systemImage: "clock")
            Label("Inställningar", systemImage: "gear")
        }
        .padding()
    }
}

// ViewThatFits — automatiskt layoutval
struct SmartLayout: View {
    var body: some View {
        ViewThatFits {
            HStack { // Prioritet 1: horisontell
                CardView()
                CardView()
            }
            VStack { // Om det inte får plats — vertikal
                CardView()
                CardView()
            }
        }
    }
}

ViewThatFits — ett kraftfullt SwiftUI-verktyg som automatiskt väljer den första underordnade vyn som får plats i tillgängligt utrymme utan att kapas. Detta eliminerar behovet av att explicit kontrollera Size Classes för enkla adaptiva växlingar. AnyLayout gör det möjligt att ändra layouttyp (HStack/VStack) utan if-else i vykroppen.

Adaptive Layout i Jetpack Compose

Jetpack Compose använder WindowSizeClass (från Material 3 adaptive-biblioteket) och BoxWithConstraints för adaptiv layout. Till skillnad från XML-kvalificerare bestämmer Compose fönsterstorleken vid körning och återuppbygger UI reaktivt vid konfigurationsändringar.

kotlin
// Jetpack Compose-anpassning via BoxWithConstraints
@Composable
fun AdaptiveList(items: List<String>) {
    BoxWithConstraints {
        val width = maxWidth

        if (width >= 600.dp) {
            // Surfplatta: två kolumner (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: en kolumn med navigering
            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 tillhandahåller färdiga komponenter: ListDetailScaffold, SupportingPaneScaffold, NavigationSuiteScaffold. Dessa komponenter anpassar sig automatiskt till WindowWidthSizeClass — växlar mellan stacked (COMPACT) och side-by-side (MEDIUM/EXPANDED). Utvecklaren behöver bara välja mönster och skicka innehållspaneler.

Vanliga frågor

Vad är skillnaden mellan sw600dp och w600dp?

sw600dp (smallestWidth) — minsta skärmbredd oavsett orientering. Fungerar alltid på 7"+ surfplattor. w600dp (available width) — tillgänglig bredd med hänsyn till aktuell orientering. Fungerar på telefoner i landskap, där bredden kan överstiga 600 dp.

Vad är UITraitCollection i iOS?

UITraitCollection — ett iOS-objekt som innehåller miljöegenskaper: size classes (horizontal/vertical), display scale, force touch capability, user interface idiom (iPhone/iPad), color gamut, accessibility settings. Det överförs från UIApplication genom vyerarkin och ändras när orientering eller fönsterstorlek ändras.

Hur anpassar jag appen för vikbara enheter?

Använd Jetpack WindowManager (Android) och UIScreen nativeBounds + traitCollection (iOS). Vikbara enheter ändrar Size Class vid utfällning. En arkitektur baserad på kanoniska layouter (list-detail, supporting pane) med reaktiv prenumeration på fönsterstorleksändringar utan omstart av Activity rekommenderas.

Behöver jag skapa separata storyboards för iPad?

Nej. Använd en storyboard med Size Classes begränsningsvariationer eller programmatisk layout via UIKit. SwiftUI kräver inga storyboards alls. Separata storyboards för iPad skapar duplicering och komplicerar underhåll — det är bättre att anpassa en layout via Size Classes.

Vad är canonical layouts i Material 3?

Canonical layouts — färdiga adaptiva layoutmönster från Google: list-detail (lista + detaljer), supporting pane (innehåll + åtgärdspanel), feed (kort). Varje mönster stöder tre tillstånd (compact/medium/expanded) och är implementerat i Material 3 Adaptive-biblioteket via ListDetailScaffold och SupportingPaneScaffold.

Sammanfattning

  • Adaptive Layout — diskret växling av layouter vid breakpoint-punkter för olika enheter
  • Size Classes iOS (Compact/Regular) bestämmer layout för iPhone, iPad och Split View
  • UITraitCollection — mekanism för att överföra miljöegenskaper genom vyerarkin
  • sw600dp — Android-kvalificerare för minsta bredd för 7"+ surfplattor
  • w600dp — kvalificerare för tillgänglig bredd som aktiveras vid rotation till landskap
  • Jetpack WindowManager tillhandahåller WindowSizeClass (WindowWidthSizeClass: COMPACT/MEDIUM/EXPANDED)
  • Material 3 Adaptive — färdiga komponenter med automatisk anpassning (ListDetailScaffold)

Vi utvecklar en mobil applikation nyckelfärdigt

IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.

Diskutera projektet

Läs också