1. 定义数据对象
创建一个Model文件夹,并在其中添加Bag文件,内容如下:
rust
struct Bag: Identifiable, Codable, Hashable {
let id: Int
let name: String
let title: String
let desc: String
}
2. 通过JSON文件加载数据
2.1. 定义ViewModel
在Model文件夹下新建一个ViewModel文件,内容如下:
typescript
import Foundation
class BagViewModel: ObservableObject {
@Published var bags: [Bag] = []
init() {
load()
}
func load() {
if let url = Bundle.main.url(forResource: "bags", withExtension: "json"),
let data = try? Data(contentsOf: url),
let bags = try? JSONDecoder().decode([Bag].self, from: data) {
self.bags = bags
}
}
}
这里声明了一个 ObserverableObject,用来加载JSON文件中的内容,并用于UI界面的展示,后面会讲到应用场景。
这个BagViewModel对象中定一个了一个bags的Bag数组对象,用来保存从文件中读取的数据。
2.2. 创建JSON文件
在根目录下创建一个bag.json的文件,并根据之前的Bag模型来定义数据,数据案例如下:
css
[ { "id": 1, "name": "Speedy", "title": "LV x YK SPEEDY BANDOULIÈRE 20 手袋", "desc": "路易威登 x 草间弥生合作系列推出 LV x YK Speedy Bandoulière 20 手袋,迸发无限波点主题的创意能量。鲜明波点经由丝印工艺呈现于 Monogram Empreinte 牛皮革之上,呼应这位知名日本艺术家作品中的一贯主题,礼赞艺术思维与精湛匠艺的结合。南瓜形状装饰再添草间弥生的视觉名片。" }, { "id": 2, "name": "Neonoe", "title": "NÉONOÉ 中号手袋 大象灰/奶白色 压纹粒面牛皮", "desc": "本款 NéoNoé 中号水桶包将 LV 字母和 Monogram 花卉先印染后压印于皮革,以瞩目观感迷惑视觉。内袋可妥善安置贵重物品,可调节肩带实现肩背或斜挎。" }]
本文件的加载与上文提到的
less
Bundle.main.url(forResource: "bags", withExtension: "json")
语句相对应。
2.3. 数据应用
先看一下界面效果,通过加载JSON文件,把bags数组呈现在右侧边栏中
less
struct ContentView: View {
@EnvironmentObject var store: ViewStore
@EnvironmentObject var viewModel: BagViewModel
var body: some View {
NavigationSplitView {
List(viewModel.bags, id: .self.id, selection: $store.selection) { bag in
HStack(alignment: .center) {
Model3D(named: bag.name, bundle: realityKitContentBundle)
.frame(width: 20, height: 20)
.frame(depth: 10, alignment: .center)
.rotation3DEffect(Angle(degrees: bag.name == "DAUPHINE" ? 179: 0), axis: .y)
.scaleEffect(x: 0.1, y: 0.1, z: 0.1)
.padding(.init(top: 0, leading: 10, bottom: 0, trailing: 10))
Text("(bag.title)")
}
}
} detail: {
ItemDetailView()
}
.environmentObject(store)
.environmentObject(viewModel)
}
}
3. 数据传递
因为我要在多个View之间传递数据,目前没想到更好的办法,就先用EnviromentObject来进行环境变量传递。
可以看到在上文中,ContentView就是用这种方式加载了环境变量viewModel和store,那么这两个环境变量是在哪里初始化的呢? 我们看一下APP的初始View:
scss
import SwiftUI
@main
struct VisionBagApp: App {
@StateObject var store = ViewStore()
@ObservedObject var viewModel = BagViewModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(store)
.environmentObject(viewModel)
}
ImmersiveSpace(id: "ImmersiveSpace") {
ImmersiveView()
.environmentObject(store)
.environmentObject(viewModel)
}
}
init() {
// Register all the custom components and systems that the app uses.
RotationComponent.registerComponent()
RotationSystem.registerSystem()
TraceComponent.registerComponent()
TraceSystem.registerSystem()
}
}
在这里完成了store和viewModel的初始化,尤其是viewModel会在这里完成JSON文件的读取。并传递到下一级的View当中。
这里我们就完成了数据模型的创建和展示,后面的文章会具体讲一下VisionOS的ImmersiveSpace和一些操作3D手势。