Om de label- en statuskolommen van een Mac-venster stabiel te houden terwijl de beschrijving uitbreidt, geef de buitenste kolommen expliciete breedtes en de beschrijving een flexibele frame. Gebruik lay-outprioriteit wanneer broers en zussen concurreren, en behoud de ideale grootte alleen op de assen die het nodig hebben.
Deze tutorial bouwt een klein, herformeerbaar venster met synthetische itemlabels en beschrijvingen. De taak is om kolommen uitgelijnd te houden terwijl langere beschrijvingen kunnen inspringen naarmate het venster smaller wordt. Alles behoort tot de voorbeeld-app; de lay-out gebruikt gewone SwiftUI-views.
Welke groottecontrole moet elke subview gebruiken?
SwiftUI-lay-out begint met een parent die een grootte voorstelt. Elk kind reageert met zijn voorkeursgrootte, en de container rangschikt de resultaten. Een nuttig startpunt is om te beslissen welke dimensies elk kind mag wijzigen. Apple legt dit voorstel-en-reactieproces uit in A guide to layout in SwiftUI.
Voor dit venster, wijzig drie rollen toe: een labelkolom met een gekozen breedte, een beschrijvingskolom die breedtewijzigingen absorbeert, en een statuskolom met een gekozen breedte. onderscheid die numerieke vaste breedte van de ideale grootte van een view: ze drukken verschillende eisen uit.
| Vereiste | Controle | Praktisch gevolg |
|---|---|---|
| Reserveer een specifieke kolombreedte | .frame(width: 96) |
De resulterende frame heeft die breedte. |
| Laat een beschrijving beschikbare breedte absorberen | .frame(minWidth: 160, maxWidth: .infinity, alignment: .leading) |
De frame accepteert de voorgestelde breedte onderworpen aan zijn minimum. |
| Behoud de ideale grootte van de inhoud op geselecteerde assen | .fixedSize(horizontal: false, vertical: true) |
Breedte kan reageren op lay-out terwijl hoogte zijn ideale grootte behoudt. |
| Bevoordeel één broer/zus tijdens ruimtetoewijzing | .layoutPriority(1) |
De parent geeft dat kind de voorkeur boven broers/zussen met lagere prioriteit. |
Apple documenteert numerieke dimensies in the fixed frame modifier, minimum- en maximumbeperkingen in the flexible frame modifier, as-specifieke ideale groottebepaling in fixedSize(horizontal:vertical:), en broeder/zus-voorkeur in layoutPriority(_:).
Een oneindig maximum is toestemming om uit te breiden binnen een voorstel; het vraagt niet om een oneindig breed venster. Het opgeven van zowel minimum- als maximumbeperkingen zorgt ervoor dat de frame de voorgestelde dimensie aanneemt, begrensd tot die beperkingen. De uitlijningsparameter positioneert het kind binnen die frame. Deze details zijn van belang wanneer de tekst zelf smaller is dan de ruimte die aan zijn kolom is toegewezen. Apple's flexible frame documentation
Bouw een compleet herformeerbaar macOS-voorbeeld
Maak een macOS-appproject in Xcode met SwiftUI en stel het implementatiedoel in op macOS 13 of later. Vervang de gegenereerde app- en content-viewdeclaraties door dit ene bestand, met slechts één app-toegangspunt. Het voorbeeld gebruikt macOS 13's vensterherformeerbaarheidscontrole. Apple's WindowResizability documentation
Het volgende is een complete app-voorbeeld. Het bevat zijn voorbeeldstrings en een schakelaar voor het wisselen tussen korte en lange beschrijvingen. De SwiftUI-app kon niet worden gecompileerd in de onderzoeksomgeving omdat een Swift-toolchain niet beschikbaar was; inspecteer het gedrag in Xcode voordat je het integreert in een bestaand project.
import SwiftUI
@main
struct ColumnAllocationApp: App {
var body: some Scene {
WindowGroup {
AllocationView()
.frame(minWidth: 420, minHeight: 260)
}
.windowResizability(.contentMinSize)
}
}
struct AllocationView: View {
@State private var showLongDescriptions = true
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("Item overview")
.font(.title2)
Toggle(
"Show longer descriptions",
isOn: $showLongDescriptions
)
ScrollView {
VStack(alignment: .leading, spacing: 16) {
AllocationRow(
label: "Item A",
summary: showLongDescriptions
? "A blue folder with several sample notes and a longer descriptive label."
: "Blue folder",
status: "Ready"
)
AllocationRow(
label: "Item B",
summary: showLongDescriptions
? "A green notebook containing a short list of synthetic example entries."
: "Green notebook",
status: "Draft"
)
}
.frame(maxWidth: .infinity, alignment: .leading)
}
}
.padding(16)
}
}
struct AllocationRow: View {
let label: String
let summary: String
let status: String
var body: some View {
HStack(alignment: .top, spacing: 12) {
Text(label)
.frame(width: 96, alignment: .leading)
Text(summary)
.fixedSize(horizontal: false, vertical: true)
.frame(
minWidth: 160,
maxWidth: .infinity,
alignment: .leading
)
Text(status)
.fixedSize(horizontal: true, vertical: false)
.frame(width: 72, alignment: .trailing)
}
}
}
De beschrijving is de enige horizontaal flexibele kolom. Zijn fixedSize-roep laat horizontale groottebepaling responsief terwijl de verticale ruimte die nodig is voor de ingepakte tekst behouden blijft. Zijn flexibele frame vult vervolgens de toegewezen kolom. De status behoudt zijn ideale tekstbreedte binnen een aparte 72-puntframe. Apple waarschuwt dat ideale-grootte-inhoud buiten de grenzen van de parent kan uitsteken, dus die statusbehandeling gaat uit van de geleverde korte waarden, Ready en Draft. Apple's fixedSize documentation
De rijen hebben geen vaste hoogte. Dit is bewust: smaller beschrijvingen kunnen meer regels vereisen. De scrollview biedt een manier om rijen te bereiken wanneer hun gecombineerde hoogte de beschikbare ruimte overschrijdt. Voeg geen beperkende rijenhoogte toe puur om de initiële rangschikking uniform te laten lijken.
Het venster gebruikt .contentMinSize, dat zijn minimumafmetingen afleidet van de inhoud en geen maximumgrootte oplegt. Hier stelt de root's 420-punt minimumbreedte een praktische ondergrens in voor het voorbeeld. Deze controle geldt voor vensters die door de app-scene worden aangemaakt. Apple's contentMinSize documentation
De declaraties die some View retourneren gebruiken Swift-oni transparante resultatentypes: hun concrete typen blijven verborgen terwijl hun conformance met View beschikbaar blijft. Je kunt de modifierketens aanpassen zonder de geneste retouretypes uit te spellen. The Swift Programming Language: Opaque and Boxed Protocol Types
Bereken het breedtebudget voordat je prioriteiten wijzigt
Een breedtebudget maakt de limieten van de lay-out expliciet. In dit voorbeeld trek je de horizontale padding, beide gaten, en de twee vaste kolommen af van de inhoudsbreedte van het venster. De rest behoort tot de beschrijving. Dit zijn illustratieve ontwerpdimensies, geen metingen van native controls.
Beschrijvingsbreedte
= inhoudsbreedte - horizontale padding - gaten - vaste kolommen
= inhoudsbreedte - (2 × 16) - (2 × 12) - 96 - 72
= inhoudsbreedte - 224
Bij 640 punten: 416 punten blijven over voor de beschrijving.
Bij 420 punten: 196 punten blijven over voor de beschrijving.
Minimum rijenbudget:
32 + 24 + 96 + 160 + 72 = 384 punten.
De rekenkunde werd gecontroleerd met een deterministische lokale berekening. Het beschrijft deze rij wanneer deze de inhoudsbreedte min de genoemde padding ontvangt; het sluit vensterdecoraties uit. De gekozen 420-punt vensterminimum laat ruimte boven de berekende 384-punt rijenminimum. Als een omringende container insets toevoegt, neem die dan ook op in het budget.
Deze berekening identificeert ook een onmogelijk verzoek. Onder het gecombineerde minimumbudget kunnen alle drie kolommen niet tegelijkertijd hun breedtes en gaten behouden. Kies een grotere minimumvensterbreedte, kleinere minimumdimensies, of een andere rangschikking. Het verhogen van de prioriteit kan geen extra ruimte creëren.
Wanneer helpt lay-outprioriteit?
Prioriteit wordt nuttig wanneer twee broers/zussen beide kunnen inkrimpen en je wilt dat er één meer ruimte behoudt. Bijvoorbeeld, een beschrijvende titel mag de voorkeur krijgen boven een secundaire annotatie. Zet .layoutPriority(1) op het kind dat deeltneemt aan die toewijzingsbeslissing van de parent. Apple stelt dat views met hogere prioriteit later inkrimpen en eerder uitrekken, met ruimte gereserveerd voor de minimumvereisten van lagere-prioriteitskinderen. Apple's layoutPriority documentation
Het complete voorbeeld heeft geen prioriteitsmodifier nodig: het toewijzingsbeleid wordt al uitgedrukt door twee vaste kolommen en één flexibele kolom. Een prioriteitswaarde is ook geen proportioneel gewicht. Het toewijzen van prioriteiten van 2 en 1 vestigt geen twee-tot-een-breedteratio; het gedocumenteerde doel is voorkeur tijdens toewijzing. Apple's layoutPriority documentation
Vermijd het toevoegen van een horizontale Spacer tussen de beschrijving en status tenzij je een aparte flexibele gap wilt. Een spacer expandeert langs de hoofdas van zijn stack en duwt broers/zussen uit elkaar. Omdat de frame van de beschrijving al breedtewijzigingen absorbeert, is die extra deelnemer hier onnodig. Apple's Spacer documentation
Wat moet er gebeuren wanneer tekst niet meer past?
Beslis afzonderlijk of elk tekstveld moet inspringen, afkappen, of een andere rangschikking vereist. De voorbeeldbeschrijvingen mogen inspringen zonder regellimiet. Het toepassen van .lineLimit(1) zou elk betrokken tekstelement op één regel beperken, wat de vereiste wijzigt. Een regellimiet van nil haalt dat kapje weg, maar verwijdert niet de beperkingen die door omsluitende frames worden opgelegd. Apple's lineLimit documentation
Voor langere statuswaarden, heroverweeg de 72-puntkolom. Verwijder horizontale ideale-groottebehoud als inspringen acceptabel is, vergroot de kolombreedte, of verplaats de status onder de beschrijving. Het behouden van .fixedSize(horizontal: true, vertical: false) terwijl de omsluitende frame kleiner wordt, kan toestaan dat de tekst buiten die frame uitsteekt. Apple's fixedSize documentation
Als een compact venster de inhoud moet herrangschikken, bieden macOS 13 en later ViewThatFits. Lever eerst een horizontale rangschikking en daarna een gestapelde rangschikking. Het selecteert het eerste kind wiens ideale grootte past op de beperkte assen, dus evalueer horizontale ruimte met ViewThatFits(in: .horizontal) wanneer breedte de beslissende factor is. Maak de laatste kandidaat bruikbaar bij je ondersteunde minimumbreedte. Apple's ViewThatFits documentation
Behandel dat als een alternatief lay-outbeleid. Het huidige voorbeeld behoudt bewust kolommen en vestigt een minimumvensterbreedte. Gebruik een gestapelde fallback wanneer het behouden van de horizontale rangschikking minder belangrijk is dan het toestaan van een smaller venster.
Hoe veranderen ingebedde AppKit- en TextKit-views de aanpak?
Als de beschrijving later een AppKit-tekstcomponent wordt, gebruik NSViewRepresentable om de view te integreren. Apple stelt expliciet dat SwiftUI de frame en bounds van de gerepresenteerde view controleert; het direct wijzigen van die eigenschappen vanuit je eigen code conflictueert met SwiftUI-lay-out. Configureer de inhoud via de representable-lifecycle en druk de uiterlijke groottebepaling uit via SwiftUI. Apple's NSViewRepresentable documentation
Op macOS 13 en later kan de sizeThatFits(_:nsView:context:)-methode van de representable de voorkeursgrootte retourneren voor een voorstel. Het kan meerdere keren worden aangeroepen met verschillende voorstellen in één lay-outpas. Het retourneren van nil vraagt het standaardgroottebepalingsalgoritme. De meting van een inpakcomponent moet rekening houden met de voorgestelde breedte in plaats van altijd de oningepakte tekstbreedte terug te geven. Apple's representable sizing documentation
Houd ongespecificeerde dimensies apart van nul: SwiftUI gebruikt een ongespecificeerd voorstel om te vragen om ideale grootte, terwijl nul de minimumgrootte kan sondieren. Een aangepaste meetroutine moet die gevallen bewust afhandelen. Apple's ProposedViewSize documentation
Binnen een AppKit-lay-out controleert compressiewestandheid hoe sterk een NSView zich verzet tegen kleiner worden dan zijn intrinsieke grootte. Inspecteer die instelling wanneer een ingepakt component compressie weerstaat, naast zijn SwiftUI-groottegedrag. Apple's setContentCompressionResistancePriority documentation
Voor een NSTextView die TextKit 2 gebruikt, gebruik het moderne textLayoutManager-pad. Apple's huidige TextKit-overzicht onderscheidt het van het verouderde layoutManager, en de migratiebegeleiding legt uit dat het openen van de verouderde eigenschap een eenrichtingscompatibiliteitsschakelaar naar TextKit 1 voor die instantie kan activeren. Het wijzigen van tekstengines is onnodig voor het toewijzen van broeder/zusterkolommen. Apple's TextKit overview, What's new in TextKit and text views
Controleer het resultaat aan de grenzen van het venster
Draai het voorbeeld en inspecteer deze voorwaarden voordat je het patroon toepast op een grotere interface:
- Verbreed het venster: de beschrijvingskolom zou moeten expanderen terwijl de label- en statusframes hun breedtes behouden.
- Vernauw het naar het ondersteunde minimum: de langere beschrijvingen zouden moeten inspringen en de rijenhoogte verhogen.
- Schakel
Show longer descriptionsuit en aan: korte beschrijvingen zouden uitgelijnd moeten blijven met dezelfde kolommen. - Vervang door een langere synthetische status: besluit of je wilt verbreeden, inspringen, of dat veld herrangschikken.
- Verminder beschikbare hoogte: bevestig dat alle rijen bereikbaar blijven.
Om te zien welke laag ruimte ontvangt, voeg tijdelijk randen toe direct voor en na de flexibele frame van de beschrijving. De binnenste rand onthult de grenzen van de tekst; de buitenste rand onthult de toegewezen kolom. Apple adviseert gekleurde randen als een praktische manier om stack-groottebepaling en padding te diagnosticeren. A guide to layout in SwiftUI
Houd het groottebeleid expliciet naarmate de interface groeit: reserveer breedtes waar uitlijning ze vereist, geef variabele inhoud een gedefinieerd flexibel gebied, en besluit hoe elk veld zich gedraagt bij de minimumbreedte. Met dat beleid en een breedtebudget kun je labels wijzigen of inhoud toevoegen zonder te gokken welke modifier een hogere prioriteit zou moeten krijgen.
Bronnen
- A guide to layout in SwiftUI — Apple
- frame(width:height:alignment:) — Apple
- frame(minWidth:idealWidth:maxWidth:minHeight:idealHeight:maxHeight:alignment:) — Apple
- fixedSize(horizontal:vertical:) — Apple
- layoutPriority(_:) — Apple
- WindowResizability — Apple
- contentMinSize — Apple
- Opaque and Boxed Protocol Types — Swift.org
- Spacer — Apple
- lineLimit(_:) — Apple
- ViewThatFits — Apple
- NSViewRepresentable — Apple
- sizeThatFits(_:nsView:context:) — Apple
- ProposedViewSize — Apple
- setContentCompressionResistancePriority(_:for:) — Apple
- TextKit — Apple
- What's new in TextKit and text views — Apple