NativeScript 8.6 为使用您喜欢的任何 JavaScript 版本开发 Vision Pro 应用提供了支持。它使您能够以令人兴奋的方式将 JavaScript 开发实践与 SwiftUI 混合使用。这篇文章将重点关注仅使用 TypeScript 以及 xml 视图标记的纯味开发。
有关其他偏好,请查看以下文章:
有关更多详细信息,请参考 使用 visionOS 开发指南。
我们提供带有
vision
标签的 npm 包,以便在您为 visionOS 开发时保持事物的清晰区分。
我们将逐步完成一个相当复杂的 visionOS“Hello World”教程,以演示 NativeScript 8.6 支持的突破性开发可能性。
选择从 Apple 教程中“下载”源代码,因为我们将直接与提供的 SwiftUI 示例集成。
npm install -g nativescript@vision
ns create myapp --vision
我们现在拥有一个可以进行开发的 Vision Pro 项目。
我们可以使用 TypeScript 构建此屏幕
我们将需要使用从 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 将为我们配置这些资产,以便我们能够在开发中使用这些资产。
我们现在可以按照以下步骤构建主窗口的背景:
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo">
<GridLayout>
<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>
</GridLayout>
</Page>
当我们运行:ns run vision --no-hmr
时,我们应该看到以下内容:
“Hello World”教程在显示主窗口内容之前使用了一种“打字机风格”的动画,当应用启动时,我们可以使用 NativeScript 动画 API 来创建这种动画,但我们也有 很多选择。例如,我们还可以将 SwiftUI 用于标题动画,以及我们由 TypeScript驱动的开发。
您可以通过两个步骤将任何 SwiftUI 集成到您的 NativeScript 项目中
<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 添加到我们的视图中,如下所示:
<Page xmlns="http://schemas.nativescript.org/tns.xsd"
navigatingTo="navigatingTo"
xmlns:sui="@nativescript/swift-ui">
<GridLayout>
<GridLayout loaded="{{ 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>
<sui:SwiftUI swiftId="title" swiftUIEvent="{{ onTitleFinished }}" class="align-middle" />
</GridLayout>
</Page>
通过定义一些 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
库以及第三方插件可以提供更多 SwiftUI 提供者,以便在 TypeScript 中使用,从而实现令人兴奋且强大的开发工作流程。
您可以参考 此仓库 以获取更多集成详细信息。
一个新的现实正在等待您! 🛸