Добавляем объём в visionOS-приложение

от автора

Разбираемся, как использовать Volume для создания иммерсивного 3D-опыта в visionOS.

Контент в visionOS-приложениях может быть представлен в виде окон (Windows) или объемных фигур (Volumes), и каждый из этих способов имеет свои отличительные особенности. Как правило, в окнах отображается 2D- или 3D-контент, как, например, медиагалерея в Photos, а объемы идеально подходят для представления иммерсивных 3D-объектов и контента.

Volume (или “объем”) представляет 3D-контент, не занимающий все доступное пространство, который пользователи могут наблюдать в любом удобном для них ракурсе. Объемы служат контейнерами для иммерсивного опыта, позволяя исследовать реалистичные представления 3D-объектов.

Volume в visionOS

Volume в visionOS

Добавление Volume в проект

В этой статье я хочу продемонстрировать вам пару шагов, которые вам необходимо будет совершить в рамках разработки приложения для visionOS с использованием Volume. Мы создадим простое visionOS-приложение, состоящее из PrimaryWindowView, которое по нажатию кнопки открывает SecondaryVolumeView.

Окно и объем, с которыми мы будем работать

Окно и объем, с которыми мы будем работать

Наше путешествие начинается с создания нового проекта в visionOS. Убедитесь, что вы настроили все необходимые конфигурации и присвоили проекту соответствующее имя.

При настройке Immersive Space Renderer (рендерер иммерсивного пространства) выберите опцию None. Хотя Apple и предлагает шаблоны для всех возможных иммерсивных пространств, выбор опции None сейчас позволит нам разобраться в основных концепциях с нуля.

Xcode – Настройка Immersive Space Renderer

Xcode – Настройка Immersive Space Renderer

Мы будем использовать файл 3D-модели .usdz — единый, самодостаточный формат файла, который содержит всю необходимую информацию для отображения 3D-объекта или сцены без каких-либо внешних зависимостей.

GlassCube

GlassCube.usdz5 KB

Перенести файл в Xcode проще простого — нужно перетащить его в основную папку проекта. Не забудьте убедиться, что при появлении окна импорта в Xcode выбрана опция «copy item if needed» (копировать элемент при необходимости).

Для начала создадим новый файл SwiftUI под названием CubeView:

import SwiftUI import RealityKit  struct CubeView: View {          @State private var angle: Angle = .degrees(0)          var body: some View {              VStack(spacing: 18.0) {             Model3D(named: "GlassCube") { model in                 switch model {                 case .empty:                     ProgressView()                                      case .success(let resolvedModel3D):                     resolvedModel3D                         .scaleEffect(0.4)                         .rotation3DEffect(angle, axis: .x)                         .rotation3DEffect(angle, axis: .y)                         .animation(.linear(duration: 18).repeatForever(), value: angle)                         .onAppear {                             angle = .degrees(359)                         }                                          case .failure(let error):                     Text(error.localizedDescription)                                      @unknown default:                     EmptyView()                 }             }         }              } }  #Preview {     CubeView() }

CubeView использует SwiftUI и RealityKit для создания представления, отображающего вращающийся 3D-куб. Он использует представление Model3D для загрузки 3D-модели с именем GlassCube. Этот куб будет непрерывно вращаться вокруг своих осей x и y.

Изначально превью куба будет отображаться в окне. В дальнейшем мы изменим код так, чтобы куб отображался в Volume.

Xcode – Превью Model3D-куба

Xcode – Превью Model3D-куба

Прежде чем двигаться дальше, давайте добавим в нашу общую ViewModel переменную secondaryVolumeIsShowing. Эта переменная будет выступать в качестве нового контроллера, определяющего, будет ли отображаться наш вторичный объем (secondaryVolume).

import Foundation  @Observable class ViewModel {     var secondaryVolumeIsShowing: Bool = false }

Сначала создадим SecondaryVolumeView. Это представление будет служить в качестве вторичного объема, обособленного от первичного окна, предоставляя дополнительное пространство для отображения контента или взаимодействия.

import SwiftUI  struct SecondaryVolumeView: View {          @Environment(ViewModel.self) private var model          var body: some View {              ZStack(alignment: .bottom) {             CubeView()                          Text("This is a volume")                 .padding()                 .glassBackgroundEffect(in: .capsule)         }         .onDisappear {             model.secondaryVolumeIsShowing.toggle()         }              } }  #Preview {     SecondaryVolumeView()         .environment(ViewModel()) }

После создания SecondaryVolumeView, мы переключим наше внимание на создание PrimaryWindowView. Это основной интерфейс, который служит главным центром взаимодействия. Нам понадобится простая кнопка, способная открывать/закрывать SecondaryWindowView, который мы только что создали.

import SwiftUI  struct PrimaryWindowView: View {          @Environment(ViewModel.self) var model          @Environment(\.openWindow) private var openWindow     @Environment(\.dismissWindow) private var dismissWindow          var body: some View {              @Bindable var model = model                  VStack(spacing: 18.0) {             Image(systemName: "1.circle.fill")                 .resizable()                 .aspectRatio(contentMode: .fit)                 .frame(width: 100,height: 100)                 .fontWeight(.light)                 .padding()                              Text("this is the primary window")                 .font(.title)                 .fontWeight(.light)                              Toggle("Open the secondary volume", isOn: $model.secondaryVolumeIsShowing)                 .toggleStyle(.button)                 .onChange(of: model.secondaryVolumeIsShowing) { _, isShowing in                     if isShowing {                         openWindow(id: "secondaryVolume")                     } else {                         dismissWindow(id: "secondaryVolume")                     }                 }         }              } }  #Preview(windowStyle: .automatic) {     PrimaryWindowView()         .environment(ViewModel()) }

И последний шаг — обновление структуры, соответствующей протоколу App, файла, автоматически генерируемого Xcode и обеспечивающего точку входа в ваше приложение.

import SwiftUI  @main struct ImplementingWindowsAndVolumesApp: App {          @State private var model = ViewModel()          var body: some Scene {              WindowGroup {             PrimaryWindowView()                 .environment(model)         }                  WindowGroup(id: "secondaryVolume") {             SecondaryVolumeView()                 .environment(model)         }         .windowStyle(.volumetric)         .defaultSize(width: 0.1, height: 0.1, depth: 0.1, in: .meters)              } }

Мы немного изменим стиль этого окна с помощью .windowStyle(.volumetric). Эта строчка изменит не только внешний вид, но и поведение окна. Вместо того чтобы быть плоским, как большинство обычных окон, это окно будет иметь некоторую глубину и объем, что придает ему 3D-подобный вид.

С помощью .defaultSize(width: 0.1, height: 0.1, depth: 0.1, in: .meters) мы устанавливаем размер этого окна по умолчанию. Мы определяем начальный размер окна в 3D-пространстве.

Первичное окно и вторичный объем

Первичное окно и вторичный объем

Вот таким нехитрым образом мы создали полностью готовую структуру для открытия 3D-объектов в Volume в нашем приложении. Следуя этой схеме, вы можете реализовать в своем приложении столько Volume-объемов, сколько вам нужно. 

Соображения по поводу объемов

Объемы и окна имеют некоторые общие характеристики, но обладают отличительными чертами, которые и определяют их роль в пространственных интерфейсах.

  • Объемы содержат 3D-объект с углом обзора 360°.

  • Объемы не отображают рамки вокруг 3D-объектов.

  • Объемы предоставляют те же элементы управления для изменения положения или закрытия, что и окна.

  • В общем пространстве (Shared Space) как объем, так и окно имеют начальное положение, определяемое системой.

  • Объем также может иметь стеклянный фон.

  • Окна достаточно универсальны для отображения 3D-элементов, но для демонстрации трехмерного контента предпочтение обычно отдается объемам.


В заключение приглашаем всех желающих на открытую онлайн-встречу «Будущее мобильной разработки на iOS», которая пройдет 20 мая в рамках курса «iOS Developer. Professional». Записаться можно по ссылке.


ссылка на оригинал статьи https://habr.com/ru/articles/814605/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *