NativeScript 8.6 支持使用您喜欢的任何 JavaScript 语言开发 Vision Pro 应用。它允许您以激动人心的方式将 JavaScript 开发实践与 SwiftUI 混合使用。本文将重点介绍 Svelte。
有关更多详细信息,请参阅 使用 visionOS 指南。
我们提供
vision
标记的 npm 包,以便在您为 visionOS 开发时保持事物清晰。
我们将逐步完成一个相当复杂的 visionOS Hello World 教程,以展示 NativeScript 8.6 实现的突破性开发可能性。
选择从 Apple 教程中“下载”源代码,因为我们将直接集成提供的 SwiftUI 示例。
npm install -g nativescript@vision
ns create myapp --vision-svelte
现在我们拥有一个可以开发的 Vision Pro 项目。
我们可以使用 Svelte 构建此屏幕
我们希望使用来自 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 项目,以便我们可以在开发中使用它们。
我们现在可以按如下方式构建主窗口的背景
<script lang="ts">
</script>
<page>
<gridLayout>
<image
src="res://SunSliver"
className="align-top w-full"
iosOverflowSafeArea={true}
stretch="aspectFit"
scaleY={1.6}
scaleX={1.6}
translateY={30}
/>
<image
src="res://EarthHalf"
className="align-bottom w-full"
iosOverflowSafeArea={true}
stretch="aspectFill"
scaleY={2}
scaleX={2}
translateY={540}
/>
</gridLayout>
</page>
当我们运行:ns run vision --no-hmr
时,我们应该看到以下内容
Hello World 教程在显示主窗口内容之前,在应用启动时使用“打字机风格”动画。我们可以使用 NativeScript 动画 API 创建此动画,但我们还有很多选择。例如,我们还可以将 SwiftUI 与我们基于 Svelte 的开发一起用于标题动画。
您可以在两个步骤中将任何 SwiftUI 集成到您的 NativeScript 项目中
<swiftUI />
。只需确保您已安装插件
npm install @nativescript/swift-ui@vision
我们可以通过将任何 .swift
代码放入 App_Resources/{platform}/src
目录中,将它移动到任何 Apple 支持的平台中,将任何 .swift
代码移动到我们的 NativeScript 应用中。
由于我们正在开发 visionOS
,因此我们可以将 Hello World 教程中的 .swift
文件移动到 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 添加到我们的视图中
<script lang="ts">
import { viewBindings as ui } from '@vision/nativescript-data';
</script>
<page>
<gridLayout>
<gridLayout on:loaded={ui.loadedBg}>
<image
src="res://SunSliver"
className="align-top w-full"
iosOverflowSafeArea={true}
stretch="aspectFit"
scaleY={1.6}
scaleX={1.6}
translateY={30}
/>
<image
src="res://EarthHalf"
className="align-bottom w-full"
iosOverflowSafeArea={true}
stretch="aspectFill"
scaleY={2}
scaleX={2}
translateY={540}
/>
</gridLayout>
<swiftUI
swiftId="title"
on:swiftUIEvent={ui.onTitleFinished}
className="align-middle"
/>
</gridLayout>
</page>
通过定义一些 on:loaded
事件来控制背景图像布局的不透明度,我们现在可以允许 SwiftUI 与我们的整个组合进行交互来实现此目的
确实很不错!
我们可以使用所有这些技术在 NativeScript 中构建 Hello World 教程的其余部分。例如,我们实际上可以将教程中的所有 .swift
资源移动到 App_Resources/visionOS/src
中,并与所有提供的 SwiftUI 进行交互。
我们可以扩展 App_Resources/visionOS/src/NativeScriptApp.swift
文件,以使用 visionOS 中可用的新场景类型来支持 体积 窗口样式和 沉浸式空间。
Apple 的 Hello World 教程使用包含所有使用的 3D 世界资产的本地 Swift 包。您可以将 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
库以及第三方插件可以为 Svelte 使用提供更多 SwiftUI 提供程序,从而实现令人兴奋且强大的开发工作流程。
您可以参考 此仓库,以获取更多集成详细信息。
一个全新的现实等待着您! 🛸