Om du vill att etikett- och statuskolumnerna i ett Mac-fönster ska ligga stilla medan beskrivningen expanderar, ger du de yttre kolumnerna uttryckliga bredder och beskrivningen en flexibel ram. Använd layoutprioritet när syskonvyer konkurrerar om utrymmet, och bevara idealstorleken endast längs de axlar där det behövs.
Den här handledningen bygger ett litet fönster som kan ändra storlek och innehåller påhittade artikelrubriker och beskrivningar. Målet är att hålla kolumnerna justerade och samtidigt låta längre beskrivningar radbrytas när fönstret blir smalare. Allt hör till exempelappen; layouten använder vanliga SwiftUI-vyer.
Vilken storlekskontroll ska varje undervy använda?
SwiftUI-layouten börjar med att en överordnad vy föreslår en storlek. Varje underordnad vy svarar med sin föredragna storlek, och containern arrangerar resultatet. En bra utgångspunkt är att bestämma vilka dimensioner varje vy får ändra. Apple beskriver den här processen med förslag och svar i En guide till layout i SwiftUI.
För det här fönstret tilldelar du tre roller: en etikettkolumn med en vald bredd, en beskrivningskolumn som tar upp breddförändringar och en statuskolumn med en vald bredd. Skilj den numeriska fasta bredden från en vyers idealstorlek: de uttrycker olika krav.
| Krav | Kontroll | Praktisk konsekvens |
|---|---|---|
| Reservera en viss kolumnbredd | .frame(width: 96) |
Den resulterande ramen får den bredden. |
| Låta en beskrivning ta upp tillgänglig bredd | .frame(minWidth: 160, maxWidth: .infinity, alignment: .leading) |
Ramen godtar den föreslagna bredden, med förbehåll för dess minsta bredd. |
| Bevara innehållets idealstorlek längs valda axlar | .fixedSize(horizontal: false, vertical: true) |
Bredden kan följa layouten medan höjden behåller sin idealstorlek. |
| Ge ett syskon företräde vid utrymmesfördelning | .layoutPriority(1) |
Den överordnade vyn prioriterar den underordnade vyn framför syskon med lägre prioritet. |
Apple dokumenterar numeriska dimensioner i modifieraren för fast ram, minimi- och maxbegränsningar i modifieraren för flexibel ram, axelspecifik idealstorlek i fixedSize(horizontal:vertical:) och syskonprioritet i layoutPriority(_:).
En oändlig maxgräns tillåter expansion inom ett förslag; den begär inte ett oändligt brett fönster. Om du anger både minimi- och maxbegränsningar antar ramen den föreslagna dimensionen, begränsad av dessa värden. Justeringsparametern placerar den underordnade vyn inuti ramen. Det här spelar roll när själva texten är smalare än utrymmet som tilldelats dess kolumn. Apples dokumentation om flexibla ramar
Bygg ett komplett macOS-exempel med ändringsbar storlek
Skapa ett macOS-approjekt i Xcode med SwiftUI och ange macOS 13 eller senare som distributionsmål. Ersätt de genererade deklarationerna för appen och innehållsvyn med den här enda filen, så att endast en startpunkt för appen finns kvar. Exemplet använder macOS 13:s kontroll för fönstrets storleksändring. Apples dokumentation om WindowResizability
Följande är ett komplett appexempel. Det innehåller exempelsträngar och en växlingsknapp för att byta mellan korta och långa beskrivningar. SwiftUI-appen kunde inte kompileras i forskningsmiljön eftersom det saknades en Swift-verktygskedja; kontrollera hur den fungerar i Xcode innan du integrerar den i ett befintligt projekt.
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)
}
}
}
Beskrivningen är den enda kolumnen som kan ändra bredd. Anropet fixedSize låter den horisontella storleken följa layouten och bevarar samtidigt det vertikala utrymme som den radbrutna texten behöver. Den flexibla ramen fyller sedan den tilldelade kolumnen. Statusen bevarar sin ideala textbredd inuti en separat ram på 72 punkter. Apple varnar för att innehåll med idealstorlek kan sträcka sig utanför den överordnade vyens gränser, så den här utformningen av statusen förutsätter att de korta värdena Ready och Draft används. Apples dokumentation om fixedSize
Raderna har ingen fast höjd. Det är avsiktligt: smalare beskrivningar kan kräva fler rader. Rullningsvyn gör det möjligt att nå raderna när deras sammanlagda höjd överstiger det tillgängliga området. Undvik att lägga till en begränsande radhöjd bara för att få den första layouten att se enhetlig ut.
Fönstret använder .contentMinSize, som härleder sina minsta dimensioner från innehållet och inte anger någon maximal storlek. Här ger rotvyns minsta bredd på 420 punkter en praktisk undre gräns för exemplet. Den här kontrollen gäller fönster som skapas av appens scen. Apples dokumentation om contentMinSize
Deklarationer som returnerar some View använder Swifts opaka resultattyper: de konkreta typerna förblir dolda, samtidigt som deras överensstämmelse med View är tillgänglig. Du kan justera modifierarkedjorna utan att skriva ut de nästlade returtyperna. The Swift Programming Language: Opaque and Boxed Protocol Types
Beräkna breddbudgeten innan du ändrar prioriteter
En breddbudget gör layoutens begränsningar tydliga. I det här exemplet drar du av den horisontella utfyllnaden, båda mellanrummen och de två fasta kolumnerna från fönstrets innehållsbredd. Det som blir kvar tillhör beskrivningen. Det här är illustrativa designmått, inte mått på inbyggda kontroller.
Beskrivningens bredd
= innehållsbredd - horisontell utfyllnad - mellanrum - fasta kolumner
= innehållsbredd - (2 × 16) - (2 × 12) - 96 - 72
= innehållsbredd - 224
Vid 640 punkter: 416 punkter återstår för beskrivningen.
Vid 420 punkter: 196 punkter återstår för beskrivningen.
Radens minsta utrymmesbudget:
32 + 24 + 96 + 160 + 72 = 384 punkter.
Beräkningen kontrollerades med en deterministisk lokal uträkning. Den beskriver den här raden när den får innehållsbredden minus den angivna utfyllnaden; fönsterdekorationer ingår inte. Den valda minsta fönsterbredden på 420 punkter lämnar utrymme utöver radens beräknade minimibredd på 384 punkter. Om en omgivande container lägger till inre marginaler ska du också räkna med dem i budgeten.
Beräkningen visar också vilket önskemål som är omöjligt. Under den sammanlagda minimibudgeten kan alla tre kolumnerna inte behålla bredd och mellanrum samtidigt. Välj en större minsta fönsterbredd, mindre minimidimensioner eller en annan layout. En högre prioritet kan inte skapa mer utrymme.
När är layoutprioritet till hjälp?
Prioritet blir användbar när två syskon kan krympa och du vill att det ena ska behålla mer utrymme. En beskrivande titel kan till exempel förtjäna företräde framför en sekundär anteckning. Lägg .layoutPriority(1) på den underordnade vyn som deltar i den överordnade vyns fördelning. Apple anger att vyer med högre prioritet krymper senare och expanderar tidigare, samtidigt som utrymme reserveras för de lägre prioriterade vyernas minimikrav. Apples dokumentation om layoutPriority
Det kompletta exemplet behöver ingen prioritetsmodifierare: fördelningsregeln uttrycks redan genom två fasta kolumner och en flexibel kolumn. Ett prioritetsvärde är inte heller en proportionell vikt. Att tilldela prioriteterna 2 och 1 skapar inte ett breddförhållande på två till ett; det dokumenterade syftet är att ange företräde vid fördelning. Apples dokumentation om layoutPriority
Undvik att lägga till en horisontell Spacer mellan beskrivningen och statusen, om du inte vill ha ett separat flexibelt mellanrum. En spacer expanderar längs stackens huvudaxel och skjuter isär syskonvyerna. Eftersom beskrivningens ram redan tar upp breddförändringar behövs inte den extra komponenten här. Apples dokumentation om Spacer
Vad ska hända när texten inte längre får plats?
Bestäm separat om varje textfält ska radbrytas, trunkeras eller kräva en annan layout. Exempelbeskrivningarna kan radbrytas utan någon radbegränsning. Om du tillämpar .lineLimit(1) begränsas varje berört textelement till en rad, vilket ändrar kravet. En radgräns på nil tar bort den begränsningen, men upphäver inte begränsningar från omslutande ramar. Apples dokumentation om lineLimit
Om statusvärdena blir längre kan du ompröva kolumnen på 72 punkter. Ta bort bevarandet av horisontell idealstorlek om radbrytning är acceptabelt, gör kolumnen bredare eller placera statusen under beskrivningen. Om du behåller .fixedSize(horizontal: true, vertical: false) och samtidigt gör den omslutande ramen smalare kan texten sträcka sig utanför ramen. Apples dokumentation om fixedSize
Om ett kompakt fönster bör arrangera om innehållet tillhandahåller macOS 13 och senare ViewThatFits. Ange först en horisontell layout och därefter en staplad layout. Den väljer det första underordnade alternativ vars idealstorlek ryms längs de begränsade axlarna, så utvärdera det horisontella utrymmet med ViewThatFits(in: .horizontal) när bredden avgör valet. Se till att det sista alternativet fungerar vid den minsta bredd som stöds. Apples dokumentation om ViewThatFits
Betrakta det som en alternativ layoutregel. Det nuvarande exemplet behåller avsiktligt kolumnerna och anger en minsta fönsterbredd. Använd en staplad reservlayout när det är viktigare att tillåta ett smalare fönster än att behålla den horisontella layouten.
Hur påverkar inbäddade AppKit- och TextKit-vyer tillvägagångssättet?
Om beskrivningen senare blir en AppKit-textkomponent använder du NSViewRepresentable för att integrera vyn. Apple säger uttryckligen att SwiftUI styr den representerade vyns frame och bounds; om du ändrar de egenskaperna direkt i din egen kod hamnar det i konflikt med SwiftUI-layouten. Konfigurera innehållet genom representerbarens livscykel och ange den yttre storleken med SwiftUI. Apples dokumentation om NSViewRepresentable
Från och med macOS 13 kan representerbarens metod sizeThatFits(_:nsView:context:) returnera den föredragna storleken för ett förslag. Den kan anropas flera gånger med olika förslag under samma layoutpass. Om du returnerar nil begär du standardalgoritmen för storleksbestämning. En radbrytande komponent bör ta hänsyn till den föreslagna bredden vid mätningen i stället för att alltid returnera den oradbrytna textens bredd. Apples dokumentation om representerbarens storleksbestämning
Håll ospecificerade dimensioner åtskilda från noll: SwiftUI använder ett ospecificerat förslag för att efterfråga idealstorlek, medan noll kan användas för att undersöka minsta storlek. En egen mätningsrutin måste hantera de fallen medvetet. Apples dokumentation om ProposedViewSize
I en AppKit-layout styr komprimeringsmotståndet hur starkt en NSView motstår att bli mindre än sin inneboende storlek. Kontrollera den inställningen om en radbrytande komponent gör motstånd mot komprimering, tillsammans med dess storleksbeteende i SwiftUI. Apples dokumentation om setContentCompressionResistancePriority
För en NSTextView som använder TextKit 2 använder du den moderna vägen textLayoutManager. Apples aktuella TextKit-översikt skiljer den från den äldre layoutManager, och migreringsvägledningen förklarar att åtkomst till den äldre egenskapen kan utlösa ett oåterkalleligt kompatibilitetsbyte till TextKit 1 för den instansen. Det behövs inget byte av textmotor för att fördela syskonkolumner. Apples TextKit-översikt, Nyheter i TextKit och textvyer
Kontrollera resultatet vid fönstrets gränser
Kör exemplet och kontrollera följande innan du använder mönstret i ett större gränssnitt:
- Gör fönstret bredare: beskrivningskolumnen ska expandera medan etikett- och statusramarna behåller sina bredder.
- Gör det smalare till den minsta bredd som stöds: de längre beskrivningarna ska radbrytas och öka radhöjden.
- Slå av och på
Show longer descriptions: korta beskrivningar ska förbli justerade med samma kolumner. - Byt ut statusen mot en längre exempeltext: avgör om fältet ska göras bredare, radbrytas eller arrangeras om.
- Minska den tillgängliga höjden: kontrollera att det fortfarande går att nå alla rader.
Om du vill se vilket lager som får utrymmet kan du tillfälligt lägga till ramar omedelbart före och efter beskrivningens flexibla ram. Den inre ramen visar textens gränser; den yttre visar den tilldelade kolumnen. Apple rekommenderar färgade ramar som ett praktiskt sätt att felsöka storleksbestämning och utfyllnad i stackar. En guide till layout i SwiftUI
Gör storleksreglerna tydliga när gränssnittet växer: reservera bredder där justeringen kräver det, ge innehåll med varierande storlek ett definierat flexibelt område och bestäm hur varje fält ska bete sig vid minsta bredd. Med dessa regler och en breddbudget kan du ändra etiketter eller lägga till innehåll utan att behöva gissa vilken modifierare som ska få högre prioritet.
Källor
- En guide till layout i 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
- Nyheter i TextKit och textvyer — Apple