Adaptive Layout — isang diskarte sa paggawa ng interface kung saan ang app ay gumagamit ng iba't ibang layout para sa iba't ibang uri ng device at oryentasyon ng screen. Hindi tulad ng responsive design, ang adaptive layout ay lumilipat sa pagitan ng mga inihandang layout sa mga breakpoint. Tinatalakay ng artikulo ang Size Classes at UITraitCollection sa iOS, mga kwalipikator na sw600dp/layout-w600dp sa Android, at nagbibigay ng mga praktikal na halimbawa.
Mga Pangunahing Punto
Adaptive Layout — isang estratehiya sa pagbuo ng interface kung saan ang developer ay gumagawa ng hiwalay na layout para sa bawat uri ng device at lumilipat sa pagitan ng mga ito batay sa mga katangian ng screen. Hindi tulad ng responsive design, kung saan ang mga elemento ay dumadaloy nang maayos, ang adaptive layout ay gumagamit ng discrete na paglipat: telepono — isang layout, tablet — iba pa, desktop — pangatlo.
Ipinakilala ng Apple ang Size Classes sa iOS 8 (2014) kasama ng mga universal app (universal binaries na gumagana sa parehong iPhone at iPad). Ipinakilala ng Google ang smallestWidth (sw
Ang mga modernong rekomendasyon ng Apple HIG at Google Material Design ay sumasang-ayon na ang adaptive layout ay kinakailangan para sa mga app na sumusuporta ng higit sa isang laki ng screen. Minamarkahan ng Google Play ang kakulangan ng adaptasyon para sa mga tablet bilang isang depekto. Ang App Store ay walang pormal na kinakailangan, ngunit ang mga gumagamit ng iPad ay umaasa ng isang native na karanasan, hindi isang stretched na iPhone app.
Size Classes — katangian ng iOS na tumutukoy sa available na espasyo bilang Compact o Regular sa bawat axis. UITraitCollection — sistema ng iOS para sa pagpapadala ng mga katangian ng kapaligiran sa pamamagitan ng hierarchy ng view: laki, scale ng screen, force touch capability, color gamut (light/dark), accessibility settings.
| Device / Oryentasyon | Horizontal | Vertical |
|---|---|---|
| iPhone portrait (lahat ng modelo) | Compact | Regular |
| iPhone Plus/Pro Max landscape | Regular | Compact |
| iPhone SE / mini landscape | Compact | Compact |
| iPad portrait | Regular | Regular |
| iPad landscape | Regular | Regular |
| iPad Split View (1/3 screen) | Compact | Regular |
Ang UITraitCollection ay nabuo ng system at ipinapadala mula sa UIApplication sa pamamagitan ng UIWindow patungo sa bawat UIView. Kapag nagbago ang oryentasyon o laki ng window (Split View), ang system ay gumagawa ng bagong UITraitCollection at tumatawag ng traitCollectionDidChange. Sa SwiftUI, ang mga pagbabago ay sinusubaybayan sa pamamagitan ng Environment Values at awtomatikong iginuhit muli ang view.
Ang adaptasyon sa pamamagitan ng UITraitCollection sa UIKit ay ipinapatupad sa pamamagitan ng pag-subscribe sa mga pagbabago ng trait collection at muling pagbuo ng layout. Ang halimbawa sa ibaba ay nagpapakita ng isang controller na lumilipat sa pagitan ng vertical stack para sa iPhone at split layout para sa iPad.
// Adaptive controller na may 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: 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
}
}Ang metodo na traitCollectionDidChange ay tinatawag sa bawat pagbabago ng traits, ngunit sinusuri lang namin ang horizontalSizeClass — ang pinakamahalagang indikator para sa pagbabago ng layout. Sa iOS 17+ inirerekomenda ang paggamit ng UIViewController.horizontalSizeClass bilang computed property para sa reaktibong pag-update nang hindi nag-subscribe sa mga pagbabago.
Ang Android ay nagbibigay ng dalawang pangunahing kwalipikator ng lapad: sw
| Kwalipikator | Kondisyon | Halimbawa ng device |
|---|---|---|
| layout-sw600dp | Pinakamababang lapad ≥ 600 dp | iPad (768 dp), Pixel C (900 dp) |
| layout-w600dp | Kasalukuyang lapad ≥ 600 dp | iPad sa Split View (600 dp), telepono sa landscape |
| layout-land | Oryentasyong landscape | Anumang device na pinaikot nang pahalang |
| layout-sw720dp | Pinakamababang lapad ≥ 720 dp | iPad Pro (833 dp), Galaxy Tab S9 (800 dp) |
Hierarchy ng mga resource sa Android: res/layout/activity_main.xml (telepono), res/layout-sw600dp/activity_main.xml (tablet 7"), res/layout-w600dp-land/activity_main.xml (telepono sa landscape). Pinipili ng Android ang pinaka-specific na kwalipikator na akma sa kasalukuyang configuration ng device. Kung walang angkop na file, ginagamit ang base resource mula sa res/layout/.
Ang modernong pag-develop ng Android ay gumagamit ng Jetpack WindowManager para matukoy ang laki ng window sa halip na direktang magbasa ng mga resource. Ito ay lalong mahalaga para sa mga foldable device, kung saan ang laki ng screen ay maaaring magbago nang dynamic (Galaxy Fold ay bumubuka mula 6.2" hanggang 7.6").
// Adaptasyon sa pamamagitan ng 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("Telepono", 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()
}
}Ang component na currentWindowAdaptiveInfo() mula sa androidx.window library ay umaangkop ng interface sa anumang device: telepono, tablet, foldable, ChromeOS. Mga klase ng lapad: COMPACT (0–600 dp), MEDIUM (600–840 dp), EXPANDED (>840 dp). Mga klase ng taas: COMPACT (0–480 dp), MEDIUM (480–900 dp), EXPANDED (>900 dp).
SwiftUI ay nagbibigay ng mga built-in na tool para sa adaptive layout nang walang direktang paggamit ng Size Classes. Ang AnyLayout, ViewThatFits, at GeometryReader ay nagbibigay-daan sa pagbuo ng mga interface na awtomatikong umaangkop sa available na espasyo.
// Adaptasyon ng SwiftUI sa pamamagitan ng AnyLayout at 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("Paborito", systemImage: "star")
Label("Kamakailan", systemImage: "clock")
Label("Mga Setting", systemImage: "gear")
}
.padding()
}
}
// ViewThatFits — awtomatikong pagpili ng layout
struct SmartLayout: View {
var body: some View {
ViewThatFits {
HStack { // Priyoridad 1: pahalang
CardView()
CardView()
}
VStack { // Kung hindi kasya — patayo
CardView()
CardView()
}
}
}
}ViewThatFits — isang makapangyarihang tool ng SwiftUI na awtomatikong pumipili ng unang child view na kasya sa available na espasyo nang hindi pinuputol. Ito ay nag-aalis ng pangangailangan na suriin nang tahasan ang Size Classes para sa simpleng adaptive na paglipat. AnyLayout ay nagpapahintulot sa pagpapalit ng uri ng layout (HStack/VStack) nang walang if-else sa body ng view.
Jetpack Compose ay gumagamit ng WindowSizeClass (mula sa Material 3 adaptive library) at BoxWithConstraints para sa adaptive na layout. Hindi tulad ng XML na mga kwalipikator, tinutukoy ng Compose ang laki ng window sa runtime at reaktibong binubuo muli ang UI kapag nagbago ang configuration.
// Adaptasyon ng Jetpack Compose sa pamamagitan ng BoxWithConstraints
@Composable
fun AdaptiveList(items: List<String>) {
BoxWithConstraints {
val width = maxWidth
if (width >= 600.dp) {
// Tablet: dalawang column (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 {
// Telepono: isang column na may nabigasyon
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() }
)
}
}
}
}Ang Material 3 Adaptive Layouts ay nagbibigay ng mga ready-made na component: ListDetailScaffold, SupportingPaneScaffold, NavigationSuiteScaffold. Ang mga component na ito ay awtomatikong umaangkop sa WindowWidthSizeClass — lumilipat sa pagitan ng stacked (COMPACT) at side-by-side (MEDIUM/EXPANDED). Ang developer ay kailangan lang pumili ng pattern at magbigay ng mga content panel.
Mga Madalas Itanong
sw600dp (smallestWidth) — pinakamababang lapad ng screen anuman ang oryentasyon. Palaging gumagana sa mga tablet na 7"+. w600dp (available width) — available na lapad na isinasaalang-alang ang kasalukuyang oryentasyon. Gumagana sa mga telepono sa landscape, kung saan ang lapad ay maaaring lumampas sa 600 dp.
UITraitCollection — object ng iOS na naglalaman ng mga katangian ng kapaligiran: size classes (horizontal/vertical), display scale, force touch capability, user interface idiom (iPhone/iPad), color gamut, accessibility settings. Ito ay ipinapadala mula sa UIApplication sa pamamagitan ng hierarchy ng view at nagbabago kapag nagbago ang oryentasyon o laki ng window.
Gamitin ang Jetpack WindowManager (Android) at UIScreen nativeBounds + traitCollection (iOS). Ang mga foldable device ay nagbabago ng Size Class kapag binuksan. Inirerekomenda ang arkitektura batay sa canonical layout (list-detail, supporting pane) na may reaktibong pag-subscribe sa mga pagbabago ng laki ng window nang hindi nire-restart ang Activity.
Hindi. Gumamit ng isang storyboard na may Size Classes constraint variations o programmatic layout sa pamamagitan ng UIKit. Ang SwiftUI ay hindi nangangailangan ng storyboards. Ang hiwalay na storyboards para sa iPad ay lumilikha ng pagdodoble at nagpapahirap sa pagpapanatili — mas mainam na iangkop ang isang layout sa pamamagitan ng Size Classes.
Canonical layouts — mga ready-made na pattern ng adaptive layout mula sa Google: list-detail (listahan + detalye), supporting pane (nilalaman + action panel), feed (cards). Bawat pattern ay sumusuporta sa tatlong estado (compact/medium/expanded) at ipinatupad sa Material 3 Adaptive library sa pamamagitan ng ListDetailScaffold at SupportingPaneScaffold.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din