Wzorce przestrzenne visionOS poza oknem
Większość aplikacji dostarczanych na visionOS trafia na platformę przez ścieżkę kompatybilności Apple „Designed for iPad”: istniejący plik binarny iPada działa jako płaski panel unoszący się w przestrzeni 3D, a deweloper zaznacza pole zamiast budować doświadczenie natywne dla visionOS. Ścieżka ta jest w porządku dla użytkownika (aplikacja działa), ale nie wykorzystuje pełnego potencjału platformy. Natywna powierzchnia visionOS udostępnia deweloperom trzy metody prezentacji (Windows, Volumes oraz Immersive Spaces) oraz strukturalne prymitywy UI (Ornaments, Attachments), których SDK iPada nie posiada. Aplikacje, które je adoptują, sprawiają wrażenie natywnych; te, które tego nie robią, postrzegane są jako iPad-na-Vision.
Ten wpis omawia słownictwo przestrzenne w odniesieniu do dokumentacji Apple. Ramą jest „co platforma faktycznie oferuje aplikacji SwiftUI”, a nie wprowadzenie do visionOS. Wpis RealityKit and the Spatial Mental Model z tego klastra omawia warstwę zawartości 3D; niniejszy wpis omawia powierzchnię SwiftUI, która ją zawiera.
TL;DR
- Aplikacje visionOS komponują trzy typy scen:
WindowGroup(Windows),WindowGroupz.windowStyle(.volumetric)(Volumes) orazImmersiveSpace(Immersive Spaces)1. - Window to płaszczyzna 2D; Volume to ograniczony region 3D; Immersive Space otacza użytkownika. Każdy ma inne reguły: Volumes mają niezmienny rozmiar po utworzeniu, Immersive Spaces wymagają jawnego otwarcia/zamknięcia, Windows zachowują się najbardziej jak na iPadzie.
- Immersja występuje w trzech stylach:
.mixed(zawartość współistnieje z pomieszczeniem),.full(pomieszczenie zastąpione wirtualnym środowiskiem),.progressive(środkowa droga z peryferyjnym uziemieniem)2. - Ornaments to płaszczyzny UI równoległe do Window i wysunięte do przodu na osi z. Tak właśnie visionOS realizuje paski narzędzi i paski zakładek3. Attachments osadzają widoki SwiftUI wewnątrz zawartości 3D w RealityView, stanowiąc most między płaskim UI a geometrią przestrzenną.
- Antywzorzec „aplikacja-panel”: dostarczanie interfejsu iPada jako Window bez adopcji Volume, Space ani Ornament. Użytkownik może korzystać z aplikacji, ale prawdziwa wartość platformy pozostaje niewykorzystana.
Trzy typy scen
Ciało App aplikacji visionOS komponuje sceny z trzech klas. Każda z nich ma odrębny model mentalny użytkownika.
Windows: płaszczyzna 2D
WindowGroup domyślnie tworzy Window 2D ze szklaną ramą visionOS. Window jest pozycjonowane w przestrzeni (system umieszcza je przed miejscem, na które patrzy użytkownik) oraz przesuwane lub zmieniane co do rozmiaru przez użytkownika za pomocą standardowych systemowych gestów. Z punktu widzenia SwiftUI, Window jest analogiem okna macOS w visionOS: płaską powierzchnią treści ze szklanym materiałem świadomym głębi.
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Domyślne Window posiada szklany materiał wokół swojej zawartości. Aplikacje, które chcą uzyskać w pełni przezroczystą powierzchnię, używają .windowStyle(.plain):
WindowGroup {
ContentView()
}
.windowStyle(.plain)
Windows w stylu plain tracą systemową szklaną ramę. Należy ich używać, gdy zawartość zapewnia własny pojemnik wizualny; w przeciwnym razie domyślna opcja jest poprawna.
Volumes: ograniczony region 3D
Volume to region 3D zawierający zawartość świadomą głębi (model, scena z wieloma obiektami, UI, które korzysta z trzeciej osi). Scena wolumetryczna jest również WindowGroup, ale z innym stylem:
WindowGroup(id: "globe") {
GlobeView()
}
.windowStyle(.volumetric)
.defaultSize(width: 0.6, height: 0.6, depth: 0.6, in: .meters)
Modyfikator .defaultSize(width:height:depth:in:) określa granice volume w jednostkach świata rzeczywistego (metrach). Domyślnie granice są ustalone w momencie otwarcia, a użytkownik może przesuwać volume, ale nie zmieniać jego rozmiaru. visionOS 2+ dodał ścieżkę opt-in poprzez .windowResizability(.contentSize) i powiązane API dla aplikacji, które chcą umożliwić użytkownikom zmianę rozmiaru volume; domyślny stały rozmiar pozostaje najczęstszym przypadkiem. Implikacja: należy starannie wybierać domyślny rozmiar, ponieważ większość volumes nie jest skalowalna, chyba że deweloper jawnie się na to zgodzi.
Właściwymi kandydatami na Volumes są aplikacje, w których ograniczenie przestrzenne stanowi część doświadczenia: wirtualna rzeźba, wokół której użytkownik chodzi, miarka przypięta do prawdziwej ściany, scena treningowa z celami rozmieszczonymi na różnych głębokościach. Aplikacje, które po prostu chcą szerszego płótna, nie zyskują na Volume; większe Window jest właściwą odpowiedzią.
Immersive Spaces: otoczenie
ImmersiveSpace to scena, która zajmuje środowisko wokół użytkownika. W przeciwieństwie do Window czy Volume (oba widoczne obok innych aplikacji w Shared Space), Immersive Space przejmuje otoczenie użytkownika i blokuje równoczesne korzystanie z okien innych aplikacji.
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
ImmersiveSpace(id: "training") {
TrainingScene()
}
.immersionStyle(selection: .constant(.mixed), in: .mixed, .progressive, .full)
}
}
Modyfikator .immersionStyle(...) wybiera poziom doświadczenia:
.mixed. Wirtualna zawartość pojawia się obok prawdziwego pomieszczenia. Stosowane w aplikacjach, w których użytkownik korzysta z obu kontekstów..progressive. Częściowa immersja, którą można regulować za pomocą Digital Crown. Użytkownik zachowuje peryferyjną świadomość pomieszczenia, podczas gdy widok centralny jest wirtualny..full. Pomieszczenie jest zastąpione wirtualnym środowiskiem. Stosowane w pełni immersyjnych doświadczeniach (medytacja, symulacje treningowe, gry).
Otwarcie Immersive Space jest jawne. Aplikacja wywołuje @Environment(\.openImmersiveSpace) z id przestrzeni; system obsługuje animację przejścia oraz zamknięcie wszelkich konfliktowych przestrzeni:
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
Button("Start Session") {
Task {
await openImmersiveSpace(id: "training")
}
}
Tylko jedna Immersive Space może być aktywna jednocześnie w danej aplikacji. Przejście między Spaces (na przykład z .mixed do .full) wymaga jawnego zamknięcia starej Space oraz otwarcia nowej.
Ornaments: płaszczyzny UI wokół Window
Ornaments to widoki SwiftUI dołączone do krawędzi Window, umieszczone nieco przed płaszczyzną Window na osi z. Tak właśnie visionOS realizuje paski narzędzi, paski zakładek oraz dodatkowe kontrolki. System używa ornaments wszędzie: kontrolki odtwarzania w TV, segmentowana kontrolka w Music, pasek narzędzi w Mail.
ContentView()
.ornament(
attachmentAnchor: .scene(.bottom),
contentAlignment: .center
) {
HStack {
Button("Previous", systemImage: "backward.fill") { ... }
Button("Play", systemImage: "play.fill") { ... }
Button("Next", systemImage: "forward.fill") { ... }
}
.padding()
.glassBackgroundEffect()
}
Parametr attachmentAnchor: określa miejsce, w którym ornament znajduje się względem Window: .scene(.top), .scene(.bottom), .scene(.leading), .scene(.trailing). Wizualne wykończenie ornament leży w gestii dewelopera; .glassBackgroundEffect() tworzy natywny dla visionOS szklany materiał, który pasuje do ramy Window.
Ornaments rozwiązują rzeczywisty problem na visionOS: umieszczanie kontrolek wewnątrz Window zatłacza zawartość; umieszczanie ich w osobnym Window zmusza użytkownika do ponownego skierowania wzroku. Ornament unosi się w peryferyjnym polu widzenia użytkownika, można go celować wzrokiem, ale nie konkuruje z główną zawartością o widok centralny.
RealityView Attachments: SwiftUI wewnątrz przestrzeni 3D
Gdy aplikacja potrzebuje widoków SwiftUI wewnątrz sceny 3D (etykieta na modelu 3D, przycisk unoszący się obok wirtualnego obiektu, odczyt pomiaru przypięty do powierzchni świata rzeczywistego), mostem jest mechanizm attachments w RealityView.
RealityView { content, attachments in
let model = ModelEntity(...)
content.add(model)
if let label = attachments.entity(for: "label") {
label.position = [0, 0.5, 0]
model.addChild(label)
}
} attachments: {
Attachment(id: "label") {
Text("Vintage Globe, 1872")
.padding()
.glassBackgroundEffect()
}
}
Domknięcie attachments: deklaruje widoki SwiftUI ze stabilnymi identyfikatorami. Wewnątrz głównego domknięcia RealityView, attachments.entity(for:) pobiera widok jako 3D Entity, który można pozycjonować w przestrzeni współrzędnych sceny. Widok uczestniczy w cyklu aktualizacji SwiftUI (zmiany stanu powodują ponowne narysowanie widoku), będąc jednocześnie renderowanym jako teksturowana płaszczyzna w scenie 3D.
Mechanizm ten jest właściwy dla każdego UI osadzonego w świecie: etykieta podążająca za poruszającym się obiektem, adnotacja pomiarowa, kontekstowy przycisk. Tworzenie widoku SwiftUI pozostaje niezmienione; pozycjonowanie 3D odbywa się na warstwie RealityView.
Antywzorzec „aplikacja-panel”
Najczęstszym błędem przy wydawaniu aplikacji na visionOS jest aplikacja-panel: aplikacja na iPada, która trafia na visionOS przez kompatybilność „Designed for iPad” i jest dostarczana jako pojedynczy Window bez Volume, Immersive Space ani Ornaments. Aplikacja działa, ale nie zasługuje na platformę.
Trzy sygnały, że aplikacja jest aplikacją-panelem:
Pojedyncza scena Window. Brak .windowStyle(.volumetric), brak zadeklarowanego ImmersiveSpace. Aplikacja jest płaską powierzchnią i niczym więcej.
Brak adopcji ornaments. Pasek zakładek aplikacji znajduje się wewnątrz zawartości Window, a nie poza nią. W rezultacie aplikacja jest bardziej zatłoczona niż natywna aplikacja visionOS o tej samej gęstości zawartości.
Brak funkcji typowo przestrzennych. Aplikacja w żaden sposób nie wykorzystuje trzeciej osi: brak modeli 3D w Volume, brak sceny środowiskowej w Space, brak UI pozycjonowanego w osi z poprzez attachments. Aplikacja robi to samo, co na iPadzie, tyle że unosząc się w powietrzu.
Aplikacje-panele nie są porażkami; są właściwym wyborem dla kategorii treści, które nie korzystają na obliczeniach przestrzennych (aplikacja czatowa, aplikacja do notatek, narzędzie ustawień). Trybem porażki jest dostarczenie aplikacji-panelu i jednoczesne pretendowanie do statusu natywnej aplikacji visionOS. Wpis Apple Platform Matrix z tego klastra argumentuje, że włączenie platformy jest decyzją produktową; w przypadku visionOS pytanie brzmi: „czy ta aplikacja powinna zasłużyć na powierzchnię przestrzenną, czy panel jest wystarczający?”
Częste błędy
Trzy wzorce, które prowadzą do słabego UX na visionOS:
Volumes, które w rzeczywistości są zawartością 2D z paddingiem głębokości. „3D” UI, które wypełnia Volume, ale renderuje płaskie płaszczyzny w jego wnętrzu, marnuje przestrzeń. Volumes są przeznaczone dla zawartości 3D; zawartość płaska należy do Window.
Styl immersji, który walczy z przypadkiem użycia. Aplikacja medytacyjna, która dostarcza wyłącznie immersję .full, zmusza użytkownika do opuszczenia swojego środowiska na krótkie sesje. Aplikacja treningowa, która dostarcza wyłącznie .mixed, nie idzie wystarczająco daleko dla w pełni skupionych ćwiczeń. Należy dopasować styl immersji do rzeczywistej sesji użytkownika.
Ornaments konkurujące z zawartością. Ornaments są z założenia peryferyjne. Ornament, który wymaga centralnej uwagi (migający kolor, animowany ruch), niweczy swój cel. Należy używać ornaments dla stabilnych, łatwych do rzucenia okiem kontrolek.
Co ten wzorzec oznacza dla aplikacji visionOS
Trzy wnioski.
-
Należy wybierać typ sceny zgodnie z modelem mentalnym użytkownika, nie według tego, co jest łatwe. Płaska lista elementów to Window. Model 3D, który użytkownik analizuje, to Volume. Otaczające środowisko to Immersive Space. Łączenie ich w jednej aplikacji (Window z Volume otwieranym na żądanie, Immersive Space dostępna z przycisku Window) jest natywnym wzorcem visionOS.
-
Należy adoptować ornaments dla pasków narzędzi i pomocniczego UI. Ornaments to sposób, w jaki visionOS komunikuje „to UI jest uzupełniające”; umieszczanie pasków narzędzi wewnątrz zawartości Window wygląda jak iPad-na-Vision. Integracja jest niewielka, a różnica wizualna duża.
-
Należy używać attachments dla UI osadzonego w świecie w RealityView. Etykiety na obiektach 3D, przyciski w pobliżu wirtualnej zawartości, kontekstowe odczyty. Most pomiędzy SwiftUI a przestrzenią 3D jest rozwiązany; trybem porażki jest jego nieużycie i zakończenie z ad-hocowym renderowaniem tekstu 3D.
Pełny klaster Apple Ecosystem: typowane App Intents; serwery MCP; pytanie o routing; Foundation Models; rozróżnienie LLM runtime vs tooling; trzy powierzchnie; wzorzec pojedynczego źródła prawdy; Two MCP Servers; hooki dla rozwoju Apple; Live Activities; kontrakt runtime watchOS; wewnętrzności SwiftUI; model mentalny przestrzenny RealityKit; dyscyplina schematu SwiftData; wzorce Liquid Glass; wieloplatformowe wydawanie; macierz platform; framework Vision; Symbol Effects; inferencja Core ML; API Writing Tools; Swift Testing; Privacy Manifest; Dostępność jako platforma; typografia SF Pro; o czym odmawiam pisać. Hub znajduje się pod adresem Apple Ecosystem Series. Szerszy kontekst dotyczący iOS z agentami AI można znaleźć w iOS Agent Development guide.
FAQ
Jaka jest różnica między Volume a Immersive Space?
Volume to ograniczony region 3D, który znajduje się w Shared Space obok innych aplikacji. Użytkownik może wokół niego chodzić, system go obramowuje, a okna innych aplikacji pozostają widoczne. Immersive Space otacza użytkownika, przejmuje środowisko i uniemożliwia równoczesne korzystanie z innych aplikacji. Volumes służą do „spójrz na tę rzecz 3D”; Spaces służą do „bądź w tym środowisku”.
Czy mogę otworzyć wiele Volumes jednocześnie?
Tak. Wiele scen WindowGroup-z-.volumetric może być otwartych jednocześnie, każda z własnym rozmiarem i zawartością. System pozycjonuje je niezależnie w przestrzeni.
Czy mogę otworzyć wiele Immersive Spaces jednocześnie?
Nie. Tylko jedna Immersive Space może być aktywna w danej aplikacji jednocześnie. Przełączanie między Spaces wymaga jawnego zamknięcia bieżącej i otwarcia nowej poprzez @Environment(\.openImmersiveSpace) oraz @Environment(\.dismissImmersiveSpace).
Czy rozmiar Volume jest naprawdę niezmienny?
Granice Volume są domyślnie ustalone w momencie otwarcia; visionOS HIG argumentuje, że Volumes reprezentują konkretną zawartość 3D z zamierzonymi granicami, a dowolna zmiana rozmiaru przez użytkownika zniekształciłaby zamierzoną skalę zawartości. visionOS 2+ dodał deweloperską opcję opt-in dla skalowalnych volumes poprzez .windowResizability(.contentSize) i powiązane API, więc aplikacje wymagające skalowalnych przez użytkownika kontenerów przestrzennych mogą o to poprosić. Większość volumes jest dostarczana z domyślnym stałym rozmiarem, który HIG nadal rekomenduje dla zawartości o określonej skali (wirtualna rzeźba, fizycznie wymiarowany model).
Jak dodać pasek zakładek do Window visionOS?
Należy użyć TabView wewnątrz Window dla zakładek osadzonych w treści (wzorzec w stylu iPada) lub użyć ornament z niestandardowymi rzędami przycisków dla peryferyjnego UI zakładek natywnego dla visionOS. Ścieżkę ornament wykorzystują własne aplikacje Apple (Music, Mail) i to ona sprawia najbardziej natywne wrażenie dla użytkowników visionOS.
Czy attachments RealityView mogą wchodzić w interakcje ze śledzeniem dłoni?
Tak. Attachments stają się encjami 3D po pozycjonowaniu i uczestniczą w tym samym systemie gestów oraz testowania trafień, co inne encje RealityKit. Gesty stuknięcia, przeciągania i najechania dołączają się do nich poprzez standardowe modyfikatory gestów SwiftUI; wpis RealityKit z tego klastra omawia wzorce integracji śledzenia dłoni.
References
-
Apple Developer: Meet SwiftUI for spatial computing (WWDC 2023 session 10109). Introduction of WindowGroup, volumetric WindowGroup, and ImmersiveSpace as the three visionOS scene types. ↩
-
Apple Developer Documentation:
ImmersionStyle. The three immersion styles (.mixed,.progressive,.full) and the.immersionStyle(selection:in:)modifier API. ↩ -
Apple Developer Documentation:
ornament(visibility:attachmentAnchor:contentAlignment:ornament:). The SwiftUI view modifier that adds an ornament UI plane to a Window with the specified anchor. ↩ -
Apple Developer: Go beyond the window with SwiftUI (WWDC 2023 session 10111). The session covering Volumes, Immersive Spaces, and the patterns for moving beyond flat panel UI on visionOS. ↩
-
Apple Developer Documentation: Creating an immersive space in visionOS with SwiftUI. The end-to-end guide to defining and opening immersive spaces. ↩