Använd sheet(item:onDismiss:content:) med ett valfritt valt objekt och skapa arket utifrån objektet som dess closure tar emot. Då får en Mac-app ett värde som avgör både om ett ark ska visas och vad det ska visa.
Den här handledningen implementerar en liten skrivbordsuppgift: klicka på ett lokalt exempelobjekt och granska dess detaljer direkt. Den innehåller också en diagnostisk sekvens för ett ark som öppnas tomt, visar föregående val eller visar inaktuell text i en AppKit-vy.
Varför använda ett objektbaserat ark?
SwiftUI erbjuder två relevanta presentationsmetoder. Den booleska överlagringen visar ett ark när dess bindning blir true. Objektöverlagringen tar emot ett valfritt identifierbart värde och skickar det värdet till innehållets closure. Välj den booleska formen för en fast panel och objektformen när panelens innehåll beror på ett val. Se Apples dokumentation för booleskt baserade ark och objektbaserade ark.
En utformning med separata egenskaper för val och presentation tillåter kombinationer som en begärd presentation utan något valt objekt. Om du kombinerar dem till ett enda valfritt värde försvinner den kombinationen ur din egen tillståndsmodell: nil betyder att inget objekt har begärts, och ett ifyllt värde representerar objektet som ska granskas. Det här är en slutsats utifrån de två API:erna, inte ett påstående om en odokumenterad schemaläggningsbugg i SwiftUI.
Om en befintlig implementation tilldelar ett valt objekt och sedan växlar en boolesk egenskap, räcker inte de två instruktionerna i sig för att fastställa varför den första presentationen misslyckas. Granska innehållets dataväg innan du hänför symtomet till timing. Den användbara frågan är om arket visar sin tillhandahållna indata, läser en annan egenskap eller visar en separat lagrad kopia.
Skapa ett komplett macOS-exempel
Skapa ett macOS-app-projekt i Xcode med SwiftUI-gränssnittet. Använd macOS 12 eller senare som distributionsmål för det här exemplet. API:et för objektbaserade ark har funnits sedan macOS 10.15, medan åtgärden för miljöbaserad stängning som används nedan kräver macOS 12. Apple dokumenterar dessa tillgänglighetsgränser för objektbaserade ark och miljövärdet för stängning.
Ersätt den genererade appdeklarationen och innehållsvyn med det här kompletta programmet. Ha bara en app-ingångspunkt i målet. Alla etiketter och beskrivningar nedan är syntetiska lokala exempeldata.
import SwiftUI
struct SampleItem: Identifiable {
let id: String
let title: String
let detail: String
}
@main
struct FirstSheetDemoApp: App {
var body: some Scene {
WindowGroup {
ItemListView()
}
}
}
struct ItemListView: View {
private let items = [
SampleItem(
id: "tile-a",
title: "Amber Tile",
detail: "A square sample with an amber finish."
),
SampleItem(
id: "tile-b",
title: "Blue Tile",
detail: "A rectangular sample with a blue finish."
)
]
@State private var presentedItem: SampleItem? = nil
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text("Sample Items")
.font(.title2)
ForEach(items) { item in
Button("Inspect \(item.title)") {
presentedItem = item
}
}
}
.padding(24)
.frame(minWidth: 360, minHeight: 200)
.sheet(item: $presentedItem) { item in
ItemDetailView(item: item)
}
}
}
struct ItemDetailView: View {
let item: SampleItem
@Environment(\.dismiss) private var dismiss
var body: some View {
VStack(alignment: .leading, spacing: 16) {
Text(item.title)
.font(.title)
Text(item.detail)
Text("Item ID: \(item.id)")
.font(.caption)
HStack {
Spacer()
Button("Done") {
dismiss()
}
}
}
.padding(24)
.frame(width: 380, height: 220)
}
}
Knappen tilldelar det klickade värdet till presentedItem. Presentations-closure skickar sedan sin egen parameter item till ItemDetailView. Lägg märke till att detaljvyn har en vanlig indataegenskap, let item, i stället för en andra tillståndsegenskap för valt objekt. Apple rekommenderar att ha en enda källa till sanningen och dela skrivskyddade värden via egenskaper, och att använda bindningar när en underordnad vy behöver skrivåtkomst. Se State.
Stängningsåtgärden hör hemma inuti ItemDetailView, där den läser arkets miljö. Apple varnar uttryckligen för att om du läser den i den presenterande vyn och anropar åtgärden från arkets innehåll används i stället den presenterande vyns miljö. Behåll deklarationen intill arkets eget innehåll, som visas här. Se dismiss.
Programmet har granskats med avseende på konsekventa indata och referenser; det har inte kompilerats eller körts här eftersom ingen Swift-verktygskedja finns tillgänglig i den här miljön. Kör följande kontroller i Xcode på din målversion av macOS.
Verifiera den första presentationen med en avsiktlig klicksekvens
Börja varje kontroll av den första presentationen med en ny start. Testning först efter att du öppnat ett annat objekt kan dölja det ursprungliga symtomet. Förväntningarna nedan kommer från exempelprogrammets uttryckliga etiketter och datatilldelningar; de är acceptanskriterier, inte rapporterade testresultat.
| Åtgärd | Förväntat innehåll i arket | Vad den kontrollerar |
|---|---|---|
Starta och klicka först på Inspect Blue Tile |
Blue Tile, dess rektangulära beskrivning och tile-b |
Den första presentationen använder det klickade objektet |
Klicka på Done och granska sedan Amber Tile |
Amber Tile, dess kvadratiska beskrivning och tile-a |
Ett senare val når innehållet |
Stäng och granska Amber Tile igen |
Samma bärnstensfärgade detaljer | Det förblir konsekvent att öppna ett objekt igen |
Starta om och granska Amber Tile först |
Bärnstensfärgade detaljer direkt | Korrektheten beror inte på vilket exempel som öppnas först |
Att visa både en titel och ett ID gör den här kontrollen mer användbar än att bara kontrollera titeln. Om ditt verkliga gränssnitt tillåter dubbla visningsnamn kan du under utvecklingen använda två syntetiska objekt med samma titel men olika ID:n och beskrivningar. Det avslöjar om valet av misstag görs utifrån visningstexten.
Diagnostisera inaktuellt innehåll från knappen till den slutliga vyn
Gå igenom kontrollerna i ordning. Varje steg ringar in var skillnaden uppstår, så att du kan ändra det ansvariga lagret utan att lägga till orelaterade lösningar för presentationen.
- Kontrollera åtgärdens indata. Sätt en brytpunkt i den klickade knappens åtgärd och granska
item.id. Bekräfta att åtgärden får den avsedda posten. - Kontrollera presentationens indata. Granska parametern
itemi arkets closure. Skicka den parametern till detaljvyn i stället för att slå upp ett separat valt objekt igen. - Kontrollera den egenskap som visas till sist. Bekräfta att titeln och beskrivningen hämtas från detaljvyns indata. Leta efter en cachad egenskap eller ett lokalt utkast som innehåller ett tidigare värde.
- Kontrollera eventuell AppKit-brygga. Om vanlig SwiftUI-text är korrekt men en inbäddad textvy är inaktuell granskar du representerbarens uppdateringsmetod.
Behåll en stabil identitet
Det objektbaserade API:et kräver Identifiable. Swift definierar protokollet utifrån stabil identitet och låter den konformerande typen och dess användare bestämma varaktighet och omfattning. Exemplet använder lagrade sträng-ID:n som skiljer de två posterna åt. Se Identifiable.
För en verklig lista väljer du en identifierare som fortsätter att identifiera samma post genom de uppdateringar som gränssnittet stöder. Undvik att härleda identiteten från en titel som kan ändras eller delas av flera objekt. Undvik också en beräknad id som skapar ett nytt UUID varje gång den läses; det skulle inte ge en stabil identitet.
Skilj visningsindata från ett redigerbart utkast
En inspektör kan visa item.title direkt. En redigerare kan avsiktligt skapa ett lokalt utkast, men ett utkast innebär ytterligare ett värde vars livslängd måste definieras. Bestäm när det skapas, om senare modelländringar ska ersätta det och vad som händer när användaren är klar eller avbryter.
Apple förklarar att SwiftUI bevarar tillståndslagring för en vyidentitet och initierar den i början av den identitetens livslängd. Att återskapa ett vyvärde skapar inte automatiskt nytt tillstånd. Att initiera lokalt tillstånd från en indata är därför ingen generell metod för att hålla det tillståndet synkroniserat med senare indata. Se Demystify SwiftUI.
För den här skrivskyddade uppgiften behåller du den vanliga indataegenskapen. Om du utökar den till redigering väljer du en uttrycklig utformning för utkast och verkställning eller en bindning till det avsedda modellvärdet. Undvik att ändra vyidentiteten enbart för att uppdatera text: en ändrad identitet ersätter tillhörande tillstånd, vilket kan kasta bort ett oavslutat utkast. Den konsekvensen följer av Apples dokumenterade modell för tillståndets livslängd.
Vad ändras när arket innehåller en AppKit-textvy?
Ett ark som innehåller en NSTextView behöver fortfarande samma dataväg för det valda objektet. Det behöver också en korrekt implementerad brygga. Apples protokoll NSViewRepresentable skiljer den ursprungliga skapelsen i makeNSView(context:) från senare konfigurering i updateNSView(_:context:). En wrapper som bara tilldelar texten när vyn skapas saknar en implementation för att tillämpa senare textindata. Se NSViewRepresentable.
För en skrivskyddad wrapper initierar du den inbyggda vyn med den tillhandahållna texten och låter dess uppdateringsmetod se till att den visade texten stämmer med den aktuella indatan när det behövs. Apple dokumenterar att SwiftUI anropar uppdateringsmetoden vid ändringar som påverkar motsvarande AppKit-vy. Se updateNSView(_:context:).
En redigerbar wrapper behöver en ytterligare policy: ersätt inte texten okritiskt vid varje uppdatering och skriv därmed över pågående redigeringar. Använd en koordinator för att kommunicera ändringar från den inbyggda vyns delegat eller target-action tillbaka till SwiftUI, som Apple beskriver i dokumentationen för representeraren. Låt SwiftUI styra den representerade vyns ram och gränser; Apple varnar för att ställa in de layoutsegenskaperna direkt.
TextKit hanterar textlagring, layout och rendering. Apple dokumenterar både den moderna NSTextLayoutManager-vägen och den äldre NSLayoutManager-vägen som exponeras av NSTextView. Dessa funktioner tillhandahåller inte arkets valda post. Säkerställ att bryggan får rätt objekttext innan du undersöker textlayouten, och kontrollera tillgängligheten separat innan du inför ytterligare TextKit-API:er. Se TextKit.
Välj den presentationsmodell som passar uppgiften
Använd den booleska överlagringen när innehållet är fast och synlighet är den enda indata som ändras. Använd objektöverlagringen för den här inspektörsuppgiften. Apple dokumenterar att en ändring av det tillhandahållna objektet kan stänga och ersätta ett befintligt ark, så betrakta inte objektbyte som ett löfte om oavbruten redigering i samma presentation. Se sheet(item:onDismiss:content:).
För en korrekt första presentation gör du det lätt att spåra den valda posten: en tilldelning i knappens åtgärd, en objektparameter i arkets closure och en indata i detaljvyn. Verifiera båda exempelobjekten efter en ny start. Om resultatet fortfarande avviker visar den diagnostiska sekvensen om skillnaden uppstår vid valet, i bevarat tillstånd eller i den inbyggda vyns uppdateringsväg.
Källor
- sheet(item:onDismiss:content:) — Apple
- sheet(isPresented:onDismiss:content:) — Apple
- State — Apple
- dismiss — Apple
- Identifiable — Apple
- Demystify SwiftUI — Apple
- NSViewRepresentable — Apple
- updateNSView(_:context:) — Apple
- TextKit — Apple