返回博客首页
← 所有文章

使用 React 开发 Vision Pro 🥽 应用

2023 年 10 月 10 日 — 作者:技术指导委员会 (TSC)

NativeScript 8.6 支持使用您喜欢的任何 JavaScript 语言开发 Vision Pro 应用。它允许您以令人兴奋的方式将 JavaScript 开发实践与 SwiftUI 混合使用。本文将重点介绍 React.

有关更多详细信息,请参阅 VisionOS 开发指南.

创建 VisionOS React 应用

我们提供带有 vision 标签的 npm 包,以便在您开发 VisionOS 时保持事物清晰。

我们将逐步介绍一个相当复杂的 VisionOS Hello World 教程,以展示 NativeScript 8.6 启用的突破性开发可能性。

选择从 Apple 教程中“下载”源代码,因为我们将直接集成提供的 SwiftUI 示例。

  1. 安装:npm install -g nativescript@vision
  2. 创建应用程序:ns create myapp --vision-react

现在我们已经准备好了一个 Vision Pro 项目,可以进行开发。

构建 Hello World 主窗口

我们可以使用 React 构建此屏幕

Vision Pro Hello World Main Window

我们将需要使用 VisionOS Hello World 教程 提供的一些资产,因此如果您已下载该项目,我们可以将一些资产移到我们的 Vision Pro 项目中以供使用。

从下载的示例中拖放以下资产

  • EarthHalf.imageset
  • GlobeHero.imageset
  • SolarBackground.imageset
  • SolarHero.imageset
  • Starfield.imageset
  • SunSliver.imageset
  • TrailGradient.imageset

App_Resources/visionOS/Assets.xcassets

当我们运行 Vision Pro 项目时,NativeScript CLI 将为我们配置这些资产与 VisionOS 项目,以便我们与开发一起使用。

我们现在可以按照以下步骤构建主窗口的背景

export function ScreenOne({ navigation }: ScreenOneProps) {
  return (
    <gridLayout>
      <image
        src="res://SunSliver"
        class="align-top w-full"
        iosOverflowSafeArea={true}
        stretch="aspectFit"
        scaleY={1.6}
        scaleX={1.6}
        translateY={30}
      />
      <image
        src="res://EarthHalf"
        class="align-bottom w-full"
        iosOverflowSafeArea={true}
        stretch="aspectFill"
        scaleY={2}
        scaleX={2}
        translateY={540}
      />
    </gridLayout>
  );
}

运行:ns run vision --no-hmr 时,我们应该看到以下内容

Vision Pro Hello World Background

将 SwiftUI 与 NativeScript 混合使用

Hello World 教程在应用程序启动显示主窗口内容之前使用“打字机样式”动画。我们可以使用 NativeScript 动画 API 来创建此动画,但我们也有很多选择。例如,我们也可以在 React 驱动的开发中使用 SwiftUI 进行标题动画。

您可以在两个步骤中将任何 SwiftUI 集成到您的 NativeScript 项目中

  1. 创建一个提供者以使用标题动画。
  2. 在您的视图标记中使用 <swiftUI />

只需确保您已安装插件,npm install @nativescript/swift-ui@vision

我们可以通过将任何 .swift 代码放在 App_Resources/{platform}/src 目录中(位于任何 Apple 支持的平台中)来将它们移动到我们的 NativeScript 应用程序中。

由于我们正在开发 visionOS,因此我们可以将 .swift 文件从 Hello World 教程移动到 App_Resources/visionOS/src 中,NativeScript CLI 将构建 .swift 文件到我们的应用程序中,以便在任何地方使用。

了解有关将 SwiftUI 与 NativeScript 集成的更多信息 此处。您也可以在 Xcode 中打开 platforms/visionos/{project}.xcodeproj 以从那里开发任何 .swift。您将在 Xcode 中的 NSNativeSources 文件夹中找到位于 App_Resources/visionOS/src 中的任何本地源代码。

Apple 教程中的 TitleText 可以如下提供给 NativeScript

import SwiftUI
import UIKit

@objc
class TitleViewProvider: UIViewController, SwiftUIProvider {
  required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
  }

  required public init() {
    super.init(nibName: nil, bundle: nil)
  }

  public override func viewDidLoad() {
    super.viewDidLoad()
    setupSwiftUIView(content: IntroText(finished: {
        self.onEvent?([:])
    }))
  }

  func updateData(data: NSDictionary) {}
  var onEvent: ((NSDictionary) -> ())?
}

private struct TitleText: View {
    var title: String
    var body: some View {
        Text(title)
            .monospaced()
            .font(.system(size: 50, weight: .bold))
    }
}

@Observable
class TitleViewModel {
    var titleText: String = ""
    var isTitleFinished: Bool = false
    var finalTitle: String = "Hello World"
}

struct IntroText: View {
    @State var model = TitleViewModel()
    var finished: (() -> Void)

    var body: some View {
        @Bindable var model = model
        
        // A hidden version of the final text keeps the layout fixed
        // while the overlaid visible version types on.
        VStack {
            // A hidden version of the final text keeps the layout fixed
            // while the overlaid visible version types on.
            TitleText(title: model.finalTitle)
                .padding(.horizontal, 70)
                .hidden()
                .overlay(alignment: .leading) {
                    TitleText(title: model.titleText)
                        .padding(.leading, 70)
                }
            Text("Discover a new way of looking at the world.")
                .font(.title)
                .opacity(model.isTitleFinished ? 1 : 0)
        }
        .typeText(
            text: $model.titleText,
            finalText: model.finalTitle,
            isFinished: $model.isTitleFinished,
            isAnimated: !model.isTitleFinished)
        .animation(.default.speed(0.25), value: model.isTitleFinished)
        .onChange(of: model.isTitleFinished) {
            if (model.isTitleFinished) {
                finished()
            }
        }
    }
}

onChange(of: model.isTitleFinished) 将通知我们的 NativeScript 应用程序何时标题动画序列完成,以便我们可以无缝淡入我们的主窗口。

然后我们可以将 SwiftUI 添加到我们的视图中,如下所示

import { viewBindings } from '@vision/nativescript-data';
const ui = viewBindings;
export function ScreenOne({ navigation }: ScreenOneProps) {
  return (
    <gridLayout>
      <gridLayout onLoaded={ui.loadedBg}>
        <image
          src="res://SunSliver"
          class="align-top w-full"
          iosOverflowSafeArea={true}
          stretch="aspectFit"
          scaleY={1.6}
          scaleX={1.6}
          translateY={30}
        />
        <image
          src="res://EarthHalf"
          class="align-bottom w-full"
          iosOverflowSafeArea={true}
          stretch="aspectFill"
          scaleY={2}
          scaleX={2}
          translateY={540}
        />
      </gridLayout>

      <swiftUI
        swiftId="title"
        onSwiftUIEvent={ui.onTitleFinished}
        className="align-middle"
      />
    <gridLayout>
  );
});

通过定义一些 onLoaded 事件来控制背景图像布局的不透明度,我们现在可以允许 SwiftUI 与我们的整个构图交互以实现此目的

确实很巧妙!

继续使用体积窗口样式和沉浸式空间

我们可以使用所有这些技术在 NativeScript 中构建 Hello World 教程的其余部分。例如,我们实际上可以将教程中的所有 .swift 资源移动到 App_Resources/visionOS/src 中,并与所有提供的 SwiftUI 进行交互。

我们可以扩展我们的 App_Resources/visionOS/src/NativeScriptApp.swift 文件以支持 体积 窗口样式和 沉浸式空间,这些空间具有 visionOS 中可用的新场景类型。

Apple 的 Hello World 教程使用一个本地 Swift 包,其中包含所有使用的 3D 世界资产。您可以将 Packages 文件夹移动到 NativeScript 项目的根目录。

使用 NativeScript 8.6,我们还可以通过以下方式修改 nativescript.config.ts 来配置项目以在本地使用该 Swift 包

// ...
ios: {
  SPMPackages: [
    {
      name: 'WorldAssets',
      libs: ['WorldAssets'],
      path: './Packages/WorldAssets' 
    },
  ]
}

这假设您将 Apple 教程中的 Packages 文件夹放在项目根目录中。本地 swift 包可以相对于项目目录进行引用。

然后,我们可以使用 Apple 教程作为指南,在我们的 App_Resources/visionOS/src/NativeScriptApp.swift 中配置更多场景

import SwiftUI
import Observation
import WorldAssets

@main
struct NativeScriptApp: App {

    // The view model.
    @State private var model = ViewModel()

    // The immersion styles for different modules.
    @State private var orbitImmersionStyle: ImmersionStyle = .mixed
    @State private var solarImmersionStyle: ImmersionStyle = .full

    var body: some Scene {
        // Your NativeScript Main Window
        NativeScriptMainWindow()
        
        // A volume that displays a globe.
        WindowGroup(id: Module.globe.name) {
            Globe()
                .environment(model)
        }
        .windowStyle(.volumetric)
        .defaultSize(width: 0.6, height: 0.6, depth: 0.6, in: .meters)

        // An immersive space that places the Earth with some of its satellites
        // in your surroundings.
        ImmersiveSpace(id: Module.orbit.name) {
            Orbit()
                .environment(model)
        }
        .immersionStyle(selection: $orbitImmersionStyle, in: .mixed)

        // An immersive Space that shows the Earth, Moon, and Sun as seen from
        // Earth orbit.
        ImmersiveSpace(id: Module.solar.name) {
            SolarSystem()
                .environment(model)
        }
        .immersionStyle(selection: $solarImmersionStyle, in: .full)
    }

    init() {
        // Register all the custom components and systems that the app uses.
        RotationComponent.registerComponent()
        RotationSystem.registerSystem()
        TraceComponent.registerComponent()
        TraceSystem.registerSystem()
        SunPositionComponent.registerComponent()
        SunPositionSystem.registerSystem()
    }
}

要添加体积和沉浸式空间,请确保您将以下设置添加到您的 App_Resources/visionOS/Info.plist

<key>UIApplicationSceneManifest</key>
<dict>
    <key>UIApplicationSupportsMultipleScenes</key>
    <true/>
</dict>

这里有什么可能?

除了已有的可能性之外,创新潜力也相当惊人,因为这只是一个全新的世界的开始。@nativescript/core 库以及第三方插件可以为 React 使用提供更多 SwiftUI 提供者,以实现令人兴奋且强大的开发工作流程。

示例仓库,体验新现实

您可以参考 此仓库 以获取更多集成详细信息。

一个新的现实等待着您! 🛸