Om te verhelpen dat SwiftUI-inhoud tijdens het vergroten of verkleinen van een Mac-venster buiten beeld raakt, bepaal je eerst of tekeningen buiten de lay-outgrenzen vallen, inhoud weigert te krimpen of een ingesloten AppKit-weergave de container niet meer volgt. Herstel vervolgens die specifieke grens.

Deze gids is bedoeld voor ontwikkelaars die de aanpasbare interface van hun eigen Mac-app willen verbeteren: labels leesbaar houden, bedieningselementen bereikbaar maken en langere inhoud kunnen scrollen. De diagnostische stappen hieronder gebruiken synthetische labels en onderscheiden SwiftUI-lay-out, venstergrootte en het teruglopen van AppKit-tekst.

Bepaal welke grens tijdens het vergroten of verkleinen niet goed werkt

De SwiftUI-lay-out begint met een groottevoorstel van een bovenliggende weergave. Een onderliggende weergave kiest daarop haar grootte; het voorstel is geen opdracht om alles binnen die rechthoek af te kappen. Apple documenteert minimum-, maximum- en ideale-groottevoorstellen in ProposedViewSize. Dit verklaart waarom een smaller venster niet noodzakelijk elke onderliggende weergave smaller maakt.

Begin met een herhaalbare test: open het venster van je app, maak het smaller tot het eerste probleem optreedt en maak het daarna weer breder. Let op het eerste element dat problemen geeft, niet op de uiteindelijke indeling. Steekt een label buiten de container? Verschuift het hele paneel? Behoudt tekst de eerdere breedte voor regelafbreking?

Voeg tijdelijk .border(Color.orange) toe aan het verdachte onderliggende element en .border(Color.blue) aan het omvattende frame of de container. Plaats de modifiers op verschillende niveaus zodat je de rechthoeken kunt vergelijken. Apple laat met deze randentechniek in de documentatie over offset zien hoe je de oorspronkelijke lay-outpositie onderscheidt van verplaatste tekeningen.

Wat je ziet Controleer eerst Handige vervolgstap
Tekeningen bewegen buiten een verder stabiele rechthoek offset, position, coördinaatberekeningen Verwijder positioneringsmodifiers één voor één
Een label blijft breed terwijl de container smaller wordt Horizontale fixedSize, expliciete breedtes Laat het label een begrensde breedte aannemen
Bedieningselementen passen niet in een horizontale rij Totale breedte van bedieningselementen en tussenruimte Bied een verticale variant aan
Onderste inhoud verdwijnt wanneer de hoogte afneemt Ontbrekend gedrag voor overlopende inhoud Maak het inhoudsgebied scrollbaar
Een gehost SwiftUI-paneel verschuift binnen AppKit Grenzen en groottebeperkingen van de hostingweergave Vergelijk de grootte van de host met de inhoudsvereisten
Een AppKit-tekstweergave wordt smaller, maar regels blijven breed Breedtevolging van de tekstcontainer Controleer de configuratie van de tekstweergave en tekstcontainer

Dit zijn diagnostische hypotheses op basis van gedocumenteerd gedrag, geen garantie dat een bepaald symptoom één specifieke oorzaak heeft. Wijzig telkens één verdachte factor en herhaal dezelfde vensterbeweging. Als het vervangen van een complex onderliggend element door Text("Sample panel") het probleem oplost, voeg dan de lagen ervan geleidelijk weer toe.

Verwijder positionerings- en grootte-instellingen die niet meebewegen met het venster

Controleer offsets en positionering op basis van het middelpunt

Een offset verschuift de weergegeven inhoud zonder de oorspronkelijke afmetingen van de weergave te veranderen. Een positieve horizontale offset kan tekeningen dus voorbij de rechterrand duwen, hoewel de bovenliggende weergave nog steeds de oorspronkelijke ruimte reserveert. Verwijder de offset tijdelijk; als de tekeningen dan binnen de container vallen, vervang de aanpassing door een indeling met stacks, padding of frame-uitlijning. Zie Apples documentatie over offsetgedrag.

position(x:y:) plaatst het middelpunt van de weergave op coördinaten in de coördinatenruimte van de bovenliggende weergave. Als je die coördinaten als de linkerbovenhoek behandelt, ontstaat er een fout die samenhangt met de grootte van het onderliggende element. Een positie die één keer is berekend, kan ook ongeschikt worden wanneer de grootte van de bovenliggende weergave verandert. Kies voor een gewoon paneel met labels en bedieningselementen liever uitlijning die de gewenste relatie met de container uitdrukt. Apple beschrijft dit gedrag op basis van het middelpunt in position(x:y:).

.frame(maxWidth: .infinity, alignment: .leading) drukt bijvoorbeeld uit dat de inhoud binnen een flexibel frame aan de leidende rand wordt uitgelijnd. Dit neemt de groottevereisten van een te groot onderliggend element niet weg. Apples documentatie over flexibele frames legt uit hoe het frame voorstellen begrenst en het onderliggende element positioneert.

Controleer vaste breedtes en tekst op ideale grootte

Controleer expliciete breedte-instellingen en horizontaal gebruik van fixedSize in de tak waar het probleem optreedt. Apple documenteert specifiek dat .fixedSize(horizontal: true, vertical: false) ervoor zorgt dat tekst de ideale breedte behoudt die nodig is voor de volledige tekenreeks, waardoor die mogelijk buiten de grenzen van de bovenliggende weergave valt. Verwijder deze horizontale vereiste voor teruglopende hoofdtekst. Als het passend is om de volledige hoogte van de teruglopende tekst te behouden, kun je .fixedSize(horizontal: false, vertical: true) overwegen. Zie fixedSize(horizontal:vertical:).

Als de verticale grootte behouden blijft, moet die hoogte ergens terechtkunnen. Een alinea die verticaal groeit in een korte, niet-scrollbare container kan nog steeds onbereikbaar worden. Gebruik een verticale scrollregio als alle tekst toegankelijk moet blijven; Apple documenteert de ondersteunde scrollassen in ScrollView.

Controleer berekeningen op basis van geometrie opnieuw

GeometryReader definieert inhoud aan de hand van de eigen grootte en coördinatenruimte en geeft de bovenliggende weergave een flexibele voorkeursgrootte terug. Door deze rond een weergave te plaatsen, verandert de lay-outstructuur; het is dus geen neutrale meetwrapper. Als het probleem begon na de introductie van geometrieberekeningen, vervang die tak dan tijdelijk door gewone stacks en frames. Apple beschrijft deze eigenschappen in GeometryReader.

Als een proportionele lay-out echt geometrie nodig heeft, leid dan afmetingen af van de huidige relevante container. Controleer eventuele status waarin een breedte wordt opgeslagen die bij de eerste weergave is vastgelegd. Controleer ook of de gemeten afmeting wordt gebruikt om dezelfde regio aan te passen die wordt gemeten; het wegnemen van die afhankelijkheid is een nuttige isolatietest.

Bouw een compacte lay-out met expliciet gedrag voor overlopende inhoud

Het volgende complete voorbeeld laat drie afzonderlijke keuzes zien: tekst loopt door naar volgende regels, bedieningselementen wisselen zo nodig van indeling en verticale overloop blijft bereikbaar. De afmetingen zijn illustratieve ontwerpkeuzes, geen door Apple voorgeschreven waarden.

Maak in Xcode een macOS SwiftUI-app, stel het implementatiedoel in op macOS 13 of hoger en vervang de gegenereerde app- en content-viewdeclaraties door dit ene bestand. Behoud slechts één @main-declaratie. Het voorbeeld gebruikt ViewThatFits en API's voor vensterherschaling die beschikbaar zijn vanaf macOS 13. Apple documenteert hun gedrag in ViewThatFits en windowResizability(_:).

import SwiftUI

@main
struct ResizeExampleApp: App {
    var body: some Scene {
        WindowGroup {
            ResizeExampleView()
                .frame(minWidth: 280, minHeight: 220)
        }
        .windowResizability(.contentMinSize)
    }
}

struct ResizeExampleView: View {
    @State private var selection = "Sample A"

    private let details = Array(
        repeating: "Sample notes wrap as the panel becomes narrower.",
        count: 12
    ).joined(separator: " ")

    var body: some View {
        ScrollView(.vertical) {
            VStack(alignment: .leading, spacing: 16) {
                Text("Sample panel")
                    .font(.title2)

                Text(details)
                    .fixedSize(horizontal: false, vertical: true)
                    .frame(maxWidth: .infinity, alignment: .leading)

                ViewThatFits(in: .horizontal) {
                    HStack(spacing: 12) {
                        Button("Choose Sample A") {
                            selection = "Sample A"
                        }
                        Button("Choose Sample B") {
                            selection = "Sample B"
                        }
                    }
                    .fixedSize(horizontal: true, vertical: false)

                    VStack(alignment: .leading, spacing: 12) {
                        Button("Choose Sample A") {
                            selection = "Sample A"
                        }
                        Button("Choose Sample B") {
                            selection = "Sample B"
                        }
                    }
                }

                Text("Selected: \(selection)")
            }
            .padding(20)
            .frame(
                minWidth: 0,
                maxWidth: .infinity,
                alignment: .leading
            )
        }
    }
}

ViewThatFits beoordeelt alternatieven in de opgegeven volgorde en kiest het eerste alternatief waarvan de ideale grootte past langs de begrensde assen. Hier kiest horizontale passing tussen de knoprij en de verticale stack. De rij behoudt bewust haar ideale horizontale grootte, zodat een samengedrukte rij niet de voorkeursoptie wordt. Zorg dat beide alternatieven dezelfde beschikbare acties bieden. Zie Apples selectieregels.

Het buitenste frame laat de inhoudskolom de beschikbare breedte volgen, terwijl de verticale scrollweergave toegang biedt wanneer regelafbreking de hoogte vergroot. Deze indeling gaat ervan uit dat de afzonderlijke bedieningselementen passen bij de gekozen minimumbreedte. Bij langere labels of grotere lettertypen kan een andere minimumgrootte of compacte indeling nodig zijn.

Dit voorbeeld is gecontroleerd op consistente declaraties en API-beschikbaarheid; het is hier niet gecompileerd omdat er geen Swift-compiler beschikbaar was. Voer het uit op macOS om het daadwerkelijke venster- en tekstgedrag te controleren.

Stel vensterlimieten in nadat je de inhoudslay-out hebt hersteld

Een bruikbare minimumgrootte voorkomt dat een interface kleiner wordt dan de ondersteunde indeling. Met .contentMinSize leidt SwiftUI de minimumgrootte van het venster af van de inhoud en stelt het geen maximumgrootte in. In het voorbeeld levert het rootframe de minimale breedte en hoogte. Zie contentMinSize.

Bepaal die minimumgrootte door de compacte indeling te testen met de langste ondersteunde labels. De minimumgrootte verhogen tot een verkeerd geplaatste weergave zichtbaar wordt, kan de oorspronkelijke lay-outfout verhullen. Verwijder eerst onbedoelde verschuivingen en te grote onderliggende elementen; stel daarna de kleinste afmetingen in waarbij de interface bruikbaar blijft voor de beoogde taak.

Als AppKit het venster beheert en SwiftUI host, controleer dan de hostinggrens afzonderlijk. Op macOS 13 en later bepaalt NSHostingView.sizingOptions welke beperkingen worden afgeleid van de SwiftUI-inhoud. Wanneer de host de contentView van het venster is, werkt deze instelling ook de minimale en maximale inhoudsgrootte van het venster bij. Apple documenteert bovendien dat de inhoud wordt gecentreerd wanneer het frame van de host afwijkt van de grootte die de SwiftUI-inhoud nodig heeft. Controleer deze relaties voordat je compenserende offsets toevoegt. Zie NSHostingView sizingOptions.

Controleer AppKit en TextKit alleen wanneer de problematische weergave ze gebruikt

Laat SwiftUI de grootte van de gerepresenteerde rootweergave bepalen

Bij een NSViewRepresentable beheert SwiftUI de frame en bounds van de gerepresenteerde AppKit-weergave. Apple waarschuwt expliciet dat het zelf instellen van deze eigenschappen conflicteert met SwiftUI en ongedefinieerd gedrag oplevert. Als je handler voor vensterherschaling herhaaldelijk het frame van de gerepresenteerde rootweergave instelt, verwijder dan dat conflicterende beheer en druk de grootte uit via de SwiftUI-grens. Zie NSViewRepresentable.

Als aangepaste metingen nodig zijn, biedt macOS 13 en later sizeThatFits(_:nsView:context:). Tijdens één lay-outpass kan deze meerdere voorstellen ontvangen; nil retourneren vraagt om standaardgroottebepaling. Baseer het resultaat op het huidige voorstel en de inhoud, niet op een eerdere vensterbreedte. Voor een niet-opgegeven voorgestelde dimensie is een bewust meetbeleid nodig; behandel die niet automatisch als nul. Zie representable-groottebepaling.

Swift biedt met optionele binding een manier om een voorgestelde dimensie te controleren en eruit te halen voordat je die gebruikt. Dit is nuttig bij het implementeren van een groottebeleid zonder optionele breedtes of hoogtes geforceerd te unwrapen. De taalwerking wordt beschreven in The Basics: Optional Binding.

Maak onderscheid tussen de breedte voor tekstterugloop en de breedte van de tekstweergave

Als het problematische onderliggende element een NSTextView is, vergelijk dan de huidige breedte ervan met de breedte van de tekstcontainer. Apple documenteert dat NSTextContainer.widthTracksTextView standaard false is; als je dit instelt op true, past de container zijn breedte aan wanneer de breedte van de tekstweergave verandert. Dit is de relevante instelling wanneer regels opnieuw moeten afbreken op basis van de breedte van de tekstweergave. Zie widthTracksTextView.

Controleer ook isHorizontallyResizable. Deze instelling bepaalt of de tekstweergave van breedte verandert om in de tekst te passen. Dat is iets anders dan tekst laten teruglopen binnen een toegewezen breedte. Voor een verticaal scrollbaar tekstgebied met teruglopende regels moeten deze twee beleidsregels op elkaar zijn afgestemd en moet de omvattende lay-out de grootte van de tekstweergave daadwerkelijk aanpassen. Zie isHorizontallyResizable.

Behoud de tekstengine die je implementatie gebruikt. Apple documenteert dat toegang tot layoutManager op een NSTextView of de tekstcontainer ervan kan overschakelen naar de compatibiliteitsmodus met NSLayoutManager. Voeg die toegang niet zomaar toe wanneer je een TextKit 2-weergave diagnosticeert. Gebruik de juiste API's voor de bestaande engine en het implementatiedoel. Zie NSTextView.

Controleer de oplossing bij wijzigingen in breedte en hoogte

Volg deze laatste stappen in je eigen app:

  1. Maak het venster langzaam smaller tot voorbij het punt waar het probleem oorspronkelijk optrad en maak het daarna weer breder.
  2. Verklein de hoogte afzonderlijk en controleer of alle benodigde inhoud bereikbaar blijft.
  3. Herhaal de test met lange synthetische labels en tekst die uit meerdere regels bestaat.
  4. Activeer elk bedieningselement in zowel de brede als de compacte indeling.
  5. Controleer bij een AppKit-tekstweergave of de tekstterugloop bij het smaller en breder maken de huidige breedte volgt.

Een geslaagde oplossing houdt de positionering gekoppeld aan de huidige container, geeft inhoud een werkbare compacte indeling en maakt overlopende inhoud toegankelijk. Zodra dit gedrag standhoudt bij herhaald vergroten en verkleinen, verwijder je de tijdelijke randen en behoud je de minimumvenstergrootte die door de interface wordt gerechtvaardigd.

Bronnen