@webspatial/platform-visionos 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/web-spatial/EventEmitter.swift +11 -11
- package/web-spatial/JSBCommand.swift +15 -3
- package/web-spatial/WebMsgCommand.swift +7 -3
- package/web-spatial/WebSpatialApp.swift +10 -10
- package/web-spatial/Window.swift +2 -2
- package/web-spatial/manager/AttachmentManager.swift +81 -0
- package/web-spatial/manager/JSBManager.swift +1 -2
- package/web-spatial/manifest.swift +1 -1
- package/web-spatial/model/SpatialApp.swift +59 -55
- package/web-spatial/model/SpatialScene.swift +97 -14
- package/web-spatial/model/Spatialized2DElement.swift +4 -5
- package/web-spatial/model/SpatializedStatic3DElement.swift +1 -1
- package/web-spatial/model/dynamic3d/SpatialComponent.swift +27 -27
- package/web-spatial/model/dynamic3d/SpatialEntity.swift +2 -2
- package/web-spatial/model/dynamic3d/SpatialMaterial.swift +15 -15
- package/web-spatial/model/dynamic3d/SpatialModelEntity.swift +10 -10
- package/web-spatial/model/dynamic3d/SpatialModelResource.swift +1 -1
- package/web-spatial/model/dynamic3d/SpatialTextureResource.swift +8 -8
- package/web-spatial/view/SpatialNavView.swift +52 -47
- package/web-spatial/view/SpatializedDynamic3DView.swift +68 -4
- package/web-spatial/view/SpatializedElementView.swift +28 -13
- package/web-spatial/view/SpatializedStatic3DView.swift +4 -6
- package/web-spatial/view/view-modifier/HideViewModifier.swift +2 -2
- package/web-spatial/webview/SpatialWebController.swift +27 -24
- package/web-spatial/webview/SpatialWebView.swift +5 -1
- package/web-spatial/webview/SpatialWebViewModel.swift +13 -7
- package/web-spatial.xcodeproj/project.pbxproj +13 -0
- package/web-spatialTests/NavigationCleanupTests.swift +33 -0
|
@@ -21,7 +21,6 @@ struct NavDivider: View {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
24
|
struct NavButton: View {
|
|
26
25
|
var action: () -> Void
|
|
27
26
|
var children: Image
|
|
@@ -55,21 +54,18 @@ struct NavButton: View {
|
|
|
55
54
|
}
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
|
|
59
57
|
struct SpatialNavView: View {
|
|
60
58
|
static let navHeight: CGFloat = SpatialScene.navHeight
|
|
61
59
|
static let minWidth: CGFloat = 400
|
|
62
60
|
var spatialScene: SpatialScene
|
|
63
|
-
var model:SpatialWebViewModel? {
|
|
64
|
-
|
|
65
|
-
return spatialScene.spatialWebViewModel
|
|
66
|
-
}
|
|
61
|
+
var model: SpatialWebViewModel? {
|
|
62
|
+
return spatialScene.spatialWebViewModel
|
|
67
63
|
}
|
|
64
|
+
|
|
68
65
|
var url: String {
|
|
69
|
-
|
|
70
|
-
return spatialScene.url
|
|
71
|
-
}
|
|
66
|
+
return spatialScene.url
|
|
72
67
|
}
|
|
68
|
+
|
|
73
69
|
@State var navWidth: CGFloat = 0
|
|
74
70
|
@State private var showCopyTip = false
|
|
75
71
|
@State private var contentHeight: CGFloat = 60
|
|
@@ -81,36 +77,40 @@ struct SpatialNavView: View {
|
|
|
81
77
|
@State private var showNav: Double = 0
|
|
82
78
|
@State private var showUrl: Double = 0
|
|
83
79
|
@Namespace var hoverNamespace
|
|
84
|
-
|
|
85
|
-
func checkButtonState(){
|
|
80
|
+
|
|
81
|
+
func checkButtonState() {
|
|
86
82
|
canGoBack = model!.getController().webview!.canGoBack
|
|
87
83
|
canGoForward = model!.getController().webview!.canGoForward
|
|
88
84
|
}
|
|
89
|
-
|
|
85
|
+
|
|
90
86
|
func goBack() {
|
|
87
|
+
spatialScene.resetForNavigation()
|
|
91
88
|
model?.getController().webview?.goBack()
|
|
92
89
|
}
|
|
93
|
-
|
|
90
|
+
|
|
94
91
|
func goForward() {
|
|
92
|
+
spatialScene.resetForNavigation()
|
|
95
93
|
model?.getController().webview?.goForward()
|
|
96
94
|
}
|
|
97
|
-
|
|
95
|
+
|
|
98
96
|
func reload() {
|
|
97
|
+
spatialScene.resetForNavigation()
|
|
99
98
|
model?.getController().webview?.reload()
|
|
100
99
|
}
|
|
101
|
-
|
|
100
|
+
|
|
102
101
|
func navigateToURL(url: URL) {
|
|
102
|
+
spatialScene.resetForNavigation()
|
|
103
103
|
model?.load(url.absoluteString)
|
|
104
104
|
}
|
|
105
|
-
|
|
105
|
+
|
|
106
106
|
func getURL() -> URL? {
|
|
107
107
|
return model?.getController().webview?.url
|
|
108
108
|
}
|
|
109
|
-
|
|
109
|
+
|
|
110
110
|
@State var canGoBack: Bool = false
|
|
111
|
-
|
|
111
|
+
|
|
112
112
|
@State var canGoForward: Bool = false
|
|
113
|
-
|
|
113
|
+
|
|
114
114
|
var navHoverGroup: HoverEffectGroup {
|
|
115
115
|
HoverEffectGroup(hoverNamespace)
|
|
116
116
|
}
|
|
@@ -126,13 +126,15 @@ struct SpatialNavView: View {
|
|
|
126
126
|
action: { self.goBack()
|
|
127
127
|
},
|
|
128
128
|
children: Image("arrow_left"),
|
|
129
|
-
clearBackGround: true
|
|
129
|
+
clearBackGround: true
|
|
130
|
+
)
|
|
130
131
|
.disabled(!(self.canGoBack))
|
|
131
132
|
NavButton(
|
|
132
133
|
action: { self.goForward()
|
|
133
134
|
},
|
|
134
135
|
children: Image("arrow_right"),
|
|
135
|
-
clearBackGround: true
|
|
136
|
+
clearBackGround: true
|
|
137
|
+
)
|
|
136
138
|
.disabled(!(self.canGoBack))
|
|
137
139
|
NavButton(action: { self.reload() }, children: Image("refresh"), clearBackGround: true)
|
|
138
140
|
NavDivider()
|
|
@@ -156,12 +158,14 @@ struct SpatialNavView: View {
|
|
|
156
158
|
NavButton(
|
|
157
159
|
action: { self.goBack()
|
|
158
160
|
},
|
|
159
|
-
children: Image("arrow_left")
|
|
161
|
+
children: Image("arrow_left")
|
|
162
|
+
)
|
|
160
163
|
.disabled(!(self.canGoBack))
|
|
161
164
|
NavButton(
|
|
162
165
|
action: { self.goForward()
|
|
163
166
|
},
|
|
164
|
-
children: Image("arrow_right")
|
|
167
|
+
children: Image("arrow_right")
|
|
168
|
+
)
|
|
165
169
|
.disabled(!(self.canGoBack))
|
|
166
170
|
NavButton(action: { self.reload() }, children: Image("refresh"))
|
|
167
171
|
NavDivider()
|
|
@@ -187,14 +191,14 @@ struct SpatialNavView: View {
|
|
|
187
191
|
self.getURL()?.absoluteString ?? ""
|
|
188
192
|
)
|
|
189
193
|
)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
.lineLimit(1)
|
|
195
|
+
.textSelection(.enabled)
|
|
196
|
+
.padding(12)
|
|
197
|
+
.frame(minWidth: 200)
|
|
198
|
+
.frame(maxWidth: 500)
|
|
199
|
+
.frame(height: 44)
|
|
200
|
+
.background(.black)
|
|
201
|
+
.cornerRadius(100)
|
|
198
202
|
NavButton(action: {
|
|
199
203
|
UIPasteboard.general.string = self.getURL()?.absoluteString ?? ""
|
|
200
204
|
showCopyTip = true
|
|
@@ -204,21 +208,22 @@ struct SpatialNavView: View {
|
|
|
204
208
|
withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 }
|
|
205
209
|
}, children: Image("copy"))
|
|
206
210
|
NavButton(
|
|
207
|
-
action: {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
action: {
|
|
212
|
+
print("open browser")
|
|
213
|
+
UIApplication.shared
|
|
214
|
+
.open(
|
|
215
|
+
URL(
|
|
216
|
+
string: url.count > 0 ? url : (
|
|
217
|
+
self.getURL()?.absoluteString ?? ""
|
|
218
|
+
)
|
|
219
|
+
)!,
|
|
220
|
+
options: [:],
|
|
221
|
+
completionHandler: nil
|
|
222
|
+
)
|
|
223
|
+
withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 }
|
|
224
|
+
},
|
|
225
|
+
children: Image("browser")
|
|
214
226
|
)
|
|
215
|
-
)!,
|
|
216
|
-
options: [:],
|
|
217
|
-
completionHandler: nil
|
|
218
|
-
)
|
|
219
|
-
withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 }
|
|
220
|
-
},
|
|
221
|
-
children: Image("browser"))
|
|
222
227
|
NavButton(action: { withAnimation(.easeInOut(duration: 0.5)) { showUrl = 0; showNav = 1 } }, children: Image("close"))
|
|
223
228
|
}
|
|
224
229
|
.popover(isPresented: $showCopyTip) {
|
|
@@ -232,8 +237,8 @@ action: {
|
|
|
232
237
|
.cornerRadius(100)
|
|
233
238
|
.opacity(showUrl)
|
|
234
239
|
}
|
|
235
|
-
.onAppear
|
|
236
|
-
model?.addStateListener(.didFinishLoad){
|
|
240
|
+
.onAppear {
|
|
241
|
+
model?.addStateListener(.didFinishLoad) {
|
|
237
242
|
self.checkButtonState()
|
|
238
243
|
}
|
|
239
244
|
}
|
|
@@ -16,10 +16,21 @@ struct SpatializedDynamic3DView: View {
|
|
|
16
16
|
SpatialTapGesture(count: 1).targetedToAnyEntity()
|
|
17
17
|
.onEnded { value in
|
|
18
18
|
if let entity = value.entity as? SpatialEntity {
|
|
19
|
-
|
|
19
|
+
// Convert local gesture coordinates into world (global) coordinates via RealityKit.
|
|
20
|
+
let globalLocation3D = entity.convert(position: SIMD3<Float>(Float(value.location3D.x), Float(value.location3D.y), Float(value.location3D.z)), to: nil)
|
|
21
|
+
let globalPoint3D = Point3D(x: Double(globalLocation3D.x), y: Double(globalLocation3D.y), z: Double(globalLocation3D.z))
|
|
22
|
+
|
|
23
|
+
spatialScene.sendWebMsg(entity.spatialId, WebSpatialTapGuestureEvent(detail: WebSpatialTapGuestureEventDetail(location3D: value.location3D, globalLocation3D: globalPoint3D)))
|
|
20
24
|
} else {
|
|
21
25
|
if let spatialEntity = SpatialEntity.findNearestParent(entity: value.entity) {
|
|
22
|
-
|
|
26
|
+
// Convert using the hit entity's coordinate space, then forward to the nearest SpatialEntity.
|
|
27
|
+
let globalLocation3D = value.entity.convert(
|
|
28
|
+
position: SIMD3<Float>(Float(value.location3D.x), Float(value.location3D.y), Float(value.location3D.z)),
|
|
29
|
+
to: nil
|
|
30
|
+
)
|
|
31
|
+
let globalPoint3D = Point3D(x: Double(globalLocation3D.x), y: Double(globalLocation3D.y), z: Double(globalLocation3D.z))
|
|
32
|
+
|
|
33
|
+
spatialScene.sendWebMsg(spatialEntity.spatialId, WebSpatialTapGuestureEvent(detail: WebSpatialTapGuestureEventDetail(location3D: value.location3D, globalLocation3D: globalPoint3D)))
|
|
23
34
|
}
|
|
24
35
|
}
|
|
25
36
|
}
|
|
@@ -76,9 +87,16 @@ struct SpatializedDynamic3DView: View {
|
|
|
76
87
|
// Always forward drag gesture events to JS
|
|
77
88
|
if let entity = value.entity as? SpatialEntity {
|
|
78
89
|
if !isDrag {
|
|
90
|
+
let globalStartLocation3D = value.entity.convert(
|
|
91
|
+
position: SIMD3<Float>(Float(value.startLocation3D.x), Float(value.startLocation3D.y), Float(value.startLocation3D.z)),
|
|
92
|
+
to: nil
|
|
93
|
+
)
|
|
94
|
+
let globalStartPoint3D = Point3D(x: Double(globalStartLocation3D.x), y: Double(globalStartLocation3D.y), z: Double(globalStartLocation3D.z))
|
|
95
|
+
|
|
79
96
|
let startEvent = WebSpatialDragStartGuestureEvent(
|
|
80
97
|
detail: .init(
|
|
81
|
-
startLocation3D: value.startLocation3D
|
|
98
|
+
startLocation3D: value.startLocation3D,
|
|
99
|
+
globalLocation3D: globalStartPoint3D
|
|
82
100
|
)
|
|
83
101
|
)
|
|
84
102
|
spatialScene.sendWebMsg(entity.spatialId, startEvent)
|
|
@@ -101,13 +119,59 @@ struct SpatializedDynamic3DView: View {
|
|
|
101
119
|
}
|
|
102
120
|
|
|
103
121
|
var body: some View {
|
|
104
|
-
RealityView(make: { content in
|
|
122
|
+
RealityView(make: { content, attachments in
|
|
105
123
|
let rootEntity = spatializedDynamic3DElement.getRoot()
|
|
106
124
|
content.add(rootEntity)
|
|
125
|
+
|
|
126
|
+
// Add existing attachments on initial creation
|
|
127
|
+
for (_, info) in spatialScene.attachmentManager.attachments {
|
|
128
|
+
if let attachmentEntity = attachments.entity(for: info.id) {
|
|
129
|
+
attachmentEntity.position = info.position
|
|
130
|
+
if let parentEntity = findSpatialEntity(info.parentEntityId) {
|
|
131
|
+
parentEntity.addChild(attachmentEntity)
|
|
132
|
+
} else {
|
|
133
|
+
rootEntity.addChild(attachmentEntity)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}, update: { _, attachments in
|
|
138
|
+
let rootEntity = spatializedDynamic3DElement.getRoot()
|
|
139
|
+
// Update attachment positions and parenting
|
|
140
|
+
for (_, info) in spatialScene.attachmentManager.attachments {
|
|
141
|
+
if let attachmentEntity = attachments.entity(for: info.id) {
|
|
142
|
+
attachmentEntity.position = info.position
|
|
143
|
+
// Re-parent if not already under the correct parent
|
|
144
|
+
if let parentEntity = findSpatialEntity(info.parentEntityId) {
|
|
145
|
+
if attachmentEntity.parent != parentEntity {
|
|
146
|
+
parentEntity.addChild(attachmentEntity)
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
// Parent entity might have been destroyed; fall back to root.
|
|
150
|
+
if attachmentEntity.parent != rootEntity {
|
|
151
|
+
rootEntity.addChild(attachmentEntity)
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}, attachments: {
|
|
157
|
+
ForEach(Array(spatialScene.attachmentManager.attachments.values)) { info in
|
|
158
|
+
Attachment(id: info.id) {
|
|
159
|
+
info.webViewModel.getView()
|
|
160
|
+
.frame(
|
|
161
|
+
width: info.size.width,
|
|
162
|
+
height: info.size.height
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
107
166
|
})
|
|
108
167
|
.simultaneousGesture(spatialTapEvent)
|
|
109
168
|
.simultaneousGesture(rotate3dEvent)
|
|
110
169
|
.simultaneousGesture(dragEvent)
|
|
111
170
|
.simultaneousGesture(magnifyEvent)
|
|
112
171
|
}
|
|
172
|
+
|
|
173
|
+
private func findSpatialEntity(_ spatialId: String) -> SpatialEntity? {
|
|
174
|
+
// Look up the SpatialEntity from the SpatialScene's spatial object registry
|
|
175
|
+
return spatialScene.findSpatialObject(spatialId)
|
|
176
|
+
}
|
|
113
177
|
}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
+
import CoreGraphics
|
|
1
2
|
import SwiftUI
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
/// zIndex() have some bug, so use zOrderBias to simulate zIndex effect
|
|
4
5
|
let zOrderBias = 0.001
|
|
5
6
|
|
|
6
7
|
final class GestureFlags {
|
|
7
8
|
var isDrag = false
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
final class TransformHolder {
|
|
12
|
+
var transform: AffineTransform3D = .identity
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
struct SpatializedElementView<Content: View>: View {
|
|
11
16
|
@Environment(SpatializedElement.self) var spatializedElement: SpatializedElement
|
|
12
17
|
@Environment(SpatialScene.self) var spatialScene: SpatialScene
|
|
@@ -15,13 +20,15 @@ struct SpatializedElementView<Content: View>: View {
|
|
|
15
20
|
var content: Content
|
|
16
21
|
|
|
17
22
|
@State private var gestureFlags = GestureFlags()
|
|
23
|
+
@State private var transformHolder = TransformHolder()
|
|
24
|
+
@State private var currentTransform: AffineTransform3D = .identity
|
|
18
25
|
|
|
19
26
|
init(parentScrollOffset: Vec2, @ViewBuilder content: () -> Content) {
|
|
20
27
|
self.parentScrollOffset = parentScrollOffset
|
|
21
28
|
self.content = content()
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
|
|
31
|
+
/// Begin Interaction
|
|
25
32
|
var gesture: some Gesture {
|
|
26
33
|
DragGesture(minimumDistance: 10)
|
|
27
34
|
.onChanged(onDragging)
|
|
@@ -29,17 +36,14 @@ struct SpatializedElementView<Content: View>: View {
|
|
|
29
36
|
.simultaneously(with:
|
|
30
37
|
RotateGesture3D()
|
|
31
38
|
.onChanged(onRotateGesture3D)
|
|
32
|
-
.onEnded(onRotateGesture3DEnd)
|
|
33
|
-
)
|
|
39
|
+
.onEnded(onRotateGesture3DEnd))
|
|
34
40
|
.simultaneously(with:
|
|
35
41
|
MagnifyGesture()
|
|
36
42
|
.onChanged(onMagnifyGesture)
|
|
37
|
-
.onEnded(onMagnifyGestureEnd)
|
|
38
|
-
)
|
|
43
|
+
.onEnded(onMagnifyGestureEnd))
|
|
39
44
|
.simultaneously(with:
|
|
40
45
|
SpatialTapGesture(count: 1)
|
|
41
|
-
.onEnded(onTapEnded)
|
|
42
|
-
)
|
|
46
|
+
.onEnded(onTapEnded))
|
|
43
47
|
}
|
|
44
48
|
|
|
45
49
|
private func onRotateGesture3D(_ event: RotateGesture3D.Value) {
|
|
@@ -64,8 +68,13 @@ struct SpatializedElementView<Content: View>: View {
|
|
|
64
68
|
|
|
65
69
|
private func onDragging(_ event: DragGesture.Value) {
|
|
66
70
|
if spatializedElement.enableDragStartGesture, !gestureFlags.isDrag {
|
|
71
|
+
let localPoint = SIMD4<Double>(event.startLocation3D.x, event.startLocation3D.y, event.startLocation3D.z, 1.0)
|
|
72
|
+
let transformedPoint = transformHolder.transform.matrix * localPoint
|
|
73
|
+
let globalPoint3D = Point3D(x: transformedPoint.x, y: transformedPoint.y, z: transformedPoint.z)
|
|
74
|
+
|
|
67
75
|
let gestureEvent = WebSpatialDragStartGuestureEvent(detail: .init(
|
|
68
|
-
startLocation3D: event.startLocation3D
|
|
76
|
+
startLocation3D: event.startLocation3D,
|
|
77
|
+
globalLocation3D: globalPoint3D
|
|
69
78
|
))
|
|
70
79
|
|
|
71
80
|
spatialScene.sendWebMsg(spatializedElement.id, gestureEvent)
|
|
@@ -92,7 +101,11 @@ struct SpatializedElementView<Content: View>: View {
|
|
|
92
101
|
|
|
93
102
|
private func onTapEnded(_ event: SpatialTapGesture.Value) {
|
|
94
103
|
if spatializedElement.enableTapGesture {
|
|
95
|
-
|
|
104
|
+
let localPoint = SIMD4<Double>(event.location3D.x, event.location3D.y, event.location3D.z, 1.0)
|
|
105
|
+
let transformedPoint = transformHolder.transform.matrix * localPoint
|
|
106
|
+
let globalPoint3D = Point3D(x: transformedPoint.x, y: transformedPoint.y, z: transformedPoint.z)
|
|
107
|
+
|
|
108
|
+
spatialScene.sendWebMsg(spatializedElement.id, WebSpatialTapGuestureEvent(detail: .init(location3D: event.location3D, globalLocation3D: globalPoint3D)))
|
|
96
109
|
}
|
|
97
110
|
}
|
|
98
111
|
|
|
@@ -101,7 +114,8 @@ struct SpatializedElementView<Content: View>: View {
|
|
|
101
114
|
let gestureEvent = WebSpatialMagnifyGuestureEvent(
|
|
102
115
|
detail: .init(
|
|
103
116
|
magnification: event.magnification
|
|
104
|
-
)
|
|
117
|
+
)
|
|
118
|
+
)
|
|
105
119
|
spatialScene.sendWebMsg(spatializedElement.id, gestureEvent)
|
|
106
120
|
}
|
|
107
121
|
}
|
|
@@ -114,7 +128,6 @@ struct SpatializedElementView<Content: View>: View {
|
|
|
114
128
|
|
|
115
129
|
// End Interaction
|
|
116
130
|
|
|
117
|
-
@ViewBuilder
|
|
118
131
|
var body: some View {
|
|
119
132
|
let transform = spatializedElement.transform
|
|
120
133
|
let translation = transform.translation
|
|
@@ -146,7 +159,9 @@ struct SpatializedElementView<Content: View>: View {
|
|
|
146
159
|
.onGeometryChange3D(for: AffineTransform3D.self) { proxy in
|
|
147
160
|
let rect3d = proxy.frame(in: .named("SpatialScene"))
|
|
148
161
|
spatialScene.sendWebMsg(spatializedElement.id, SpatiaizedContainerClientCube(origin: rect3d.origin, size: rect3d.size))
|
|
149
|
-
|
|
162
|
+
let transform = proxy.transform(in: .named("SpatialScene"))!
|
|
163
|
+
transformHolder.transform = transform
|
|
164
|
+
return transform
|
|
150
165
|
} action: { _, new in
|
|
151
166
|
spatialScene.sendWebMsg(spatializedElement.id, SpatiaizedContainerTransform(detail: new))
|
|
152
167
|
}
|
|
@@ -8,7 +8,7 @@ struct SpatializedStatic3DView: View {
|
|
|
8
8
|
private var spatializedStatic3DElement: SpatializedStatic3DElement {
|
|
9
9
|
return spatializedElement as! SpatializedStatic3DElement
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
func onLoadSuccess() {
|
|
13
13
|
spatialScene.sendWebMsg(spatializedElement.id, ModelLoadSuccess())
|
|
14
14
|
}
|
|
@@ -17,7 +17,6 @@ struct SpatializedStatic3DView: View {
|
|
|
17
17
|
spatialScene.sendWebMsg(spatializedElement.id, ModelLoadFailure())
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
@ViewBuilder
|
|
21
20
|
var body: some View {
|
|
22
21
|
let depth = spatializedElement.depth
|
|
23
22
|
let transform = spatializedStatic3DElement.modelTransform
|
|
@@ -27,14 +26,13 @@ struct SpatializedStatic3DView: View {
|
|
|
27
26
|
let x = translation.x
|
|
28
27
|
let y = translation.y
|
|
29
28
|
let z = translation.z
|
|
30
|
-
|
|
29
|
+
|
|
31
30
|
let enableGesture = spatializedElement.enableGesture
|
|
32
31
|
if let url = URL(string: spatializedStatic3DElement.modelURL) {
|
|
33
32
|
Model3D(url: url) { newPhase in
|
|
34
33
|
switch newPhase {
|
|
35
34
|
case .empty:
|
|
36
35
|
ProgressView()
|
|
37
|
-
|
|
38
36
|
case let .success(resolvedModel3D):
|
|
39
37
|
resolvedModel3D
|
|
40
38
|
.resizable(true)
|
|
@@ -42,11 +40,11 @@ struct SpatializedStatic3DView: View {
|
|
|
42
40
|
nil,
|
|
43
41
|
contentMode: .fit
|
|
44
42
|
)
|
|
45
|
-
.if(!depth.isZero){ view in view.scaledToFit3D()}
|
|
43
|
+
.if(!depth.isZero) { view in view.scaledToFit3D() }
|
|
46
44
|
.onAppear {
|
|
47
45
|
self.onLoadSuccess()
|
|
48
46
|
}
|
|
49
|
-
.if(enableGesture) { view in view.hoverEffect()}
|
|
47
|
+
.if(enableGesture) { view in view.hoverEffect() }
|
|
50
48
|
case .failure:
|
|
51
49
|
Text("").onAppear {
|
|
52
50
|
self.onLoadFailure()
|
|
@@ -2,14 +2,14 @@ import SwiftUI
|
|
|
2
2
|
|
|
3
3
|
struct HideViewModifier: ViewModifier {
|
|
4
4
|
let isHidden: Bool
|
|
5
|
-
|
|
5
|
+
func body(content: Content) -> some View {
|
|
6
6
|
content
|
|
7
7
|
.opacity(isHidden ? 0 : 1)
|
|
8
8
|
.disabled(isHidden)
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
/// Extending on View to apply to all Views
|
|
13
13
|
extension View {
|
|
14
14
|
func hidden(_ isHidden: Bool) -> some View {
|
|
15
15
|
modifier(HideViewModifier(isHidden: isHidden))
|
|
@@ -9,7 +9,7 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
9
9
|
private var openWindowInvoke: ((_ data: URL) -> WebViewElementInfo?)?
|
|
10
10
|
private var webviewStateChangeInvoke: ((_ type: SpatialWebViewState) -> Void)?
|
|
11
11
|
private var scorllUpdateInvoke: ((_ type: ScrollState, _ point: CGPoint) -> Void)?
|
|
12
|
-
private var webviewTitle: String?
|
|
12
|
+
private var webviewTitle: String?
|
|
13
13
|
private var firstLoad = true
|
|
14
14
|
private var jsbManager = JSBManager()
|
|
15
15
|
|
|
@@ -62,25 +62,29 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
/// navigation request
|
|
66
|
+
/// SpatialDiv/forcestyle/normal web link protocol
|
|
67
67
|
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) {
|
|
68
68
|
let deciside = navigationInvoke?(navigationAction.request.url!)
|
|
69
69
|
if deciside == true {
|
|
70
|
-
if !firstLoad{
|
|
70
|
+
if !firstLoad {
|
|
71
71
|
webviewStateChangeInvoke?(.didUnload)
|
|
72
72
|
}
|
|
73
73
|
firstLoad = false
|
|
74
74
|
}
|
|
75
75
|
var needAllow = deciside ?? false
|
|
76
|
-
|
|
77
|
-
if !needAllow{
|
|
78
|
-
|
|
76
|
+
|
|
77
|
+
if !needAllow {
|
|
78
|
+
// webspatial:// is an internal scheme for in-app routing between main and attachment windows.
|
|
79
|
+
// Only open non-webspatial URLs externally via the system.
|
|
80
|
+
if navigationAction.request.url?.scheme != "webspatial" {
|
|
81
|
+
UIApplication.shared.open(navigationAction.request.url!, options: [:], completionHandler: nil)
|
|
82
|
+
}
|
|
79
83
|
}
|
|
80
84
|
decisionHandler(needAllow ? .allow : .cancel)
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
/// open window request
|
|
84
88
|
func webView(
|
|
85
89
|
_ webView: WKWebView,
|
|
86
90
|
createWebViewWith configuration: WKWebViewConfiguration,
|
|
@@ -95,7 +99,7 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
95
99
|
return nil
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
/// invoke jsb
|
|
99
103
|
func userContentController(
|
|
100
104
|
_ userContentController: WKUserContentController,
|
|
101
105
|
didReceive message: WKScriptMessage, replyHandler: @escaping (Any?, String?) -> Void
|
|
@@ -104,7 +108,7 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
104
108
|
jsbManager.handlerMessage(message.body as! String, replyHandler)
|
|
105
109
|
}
|
|
106
110
|
|
|
107
|
-
|
|
111
|
+
/// custom scheme request
|
|
108
112
|
func webView(_ webView: WKWebView, start urlSchemeTask: any WKURLSchemeTask) {
|
|
109
113
|
print("urlSchemeTask")
|
|
110
114
|
let url = urlSchemeTask.request.url
|
|
@@ -221,8 +225,8 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
221
225
|
scorllUpdateInvoke = nil
|
|
222
226
|
model = nil
|
|
223
227
|
}
|
|
224
|
-
|
|
225
|
-
private var state:SpatialWebViewState?
|
|
228
|
+
|
|
229
|
+
private var state: SpatialWebViewState?
|
|
226
230
|
|
|
227
231
|
func destroyView() {
|
|
228
232
|
stopObserving()
|
|
@@ -236,15 +240,15 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
236
240
|
webviewStateChangeInvoke?(.didDestroyView)
|
|
237
241
|
}
|
|
238
242
|
}
|
|
239
|
-
|
|
243
|
+
|
|
240
244
|
private var isPageLoaded = false
|
|
241
|
-
|
|
245
|
+
|
|
242
246
|
private var jsQueue: [String] = []
|
|
243
|
-
|
|
247
|
+
|
|
244
248
|
private func enqueueJS(_ js: String) {
|
|
245
249
|
jsQueue.append(js)
|
|
246
250
|
}
|
|
247
|
-
|
|
251
|
+
|
|
248
252
|
private func flushJSQueue() {
|
|
249
253
|
guard !jsQueue.isEmpty else { return }
|
|
250
254
|
let combined = jsQueue.joined(separator: ";")
|
|
@@ -253,7 +257,7 @@ class SpatialWebController: NSObject, WKNavigationDelegate, WKScriptMessageHandl
|
|
|
253
257
|
}
|
|
254
258
|
|
|
255
259
|
func callJS(_ js: String) {
|
|
256
|
-
if webview != nil
|
|
260
|
+
if webview != nil, isPageLoaded {
|
|
257
261
|
webview!.evaluateJavaScript(js)
|
|
258
262
|
} else {
|
|
259
263
|
enqueueJS(js)
|
|
@@ -268,13 +272,13 @@ enum ScrollState {
|
|
|
268
272
|
case end
|
|
269
273
|
}
|
|
270
274
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
/// extend webview to support file://
|
|
276
|
+
@available(iOS 11.0, *)
|
|
277
|
+
extension WKWebView {
|
|
274
278
|
/// WKWebView, Support setting file scheme in configuration
|
|
275
279
|
public private(set) static var isEnableFileSupport = false
|
|
276
280
|
public static func enableFileScheme() {
|
|
277
|
-
|
|
281
|
+
// This method supports adapting supported files through Configuration, but cannot be cancelled (Configuration is immutable).
|
|
278
282
|
if !isEnableFileSupport {
|
|
279
283
|
switchHandlesURLScheme()
|
|
280
284
|
}
|
|
@@ -292,9 +296,8 @@ enum ScrollState {
|
|
|
292
296
|
}
|
|
293
297
|
|
|
294
298
|
/// Return true if WKWebview supports handling this protocol, but WKWebview supports HTTP by default, so return false to support using custom HTTP Handler
|
|
295
|
-
@objc private dynamic
|
|
296
|
-
static func wrapHandles(urlScheme: String) -> Bool {
|
|
299
|
+
@objc private dynamic static func wrapHandles(urlScheme: String) -> Bool {
|
|
297
300
|
if urlScheme == "file" { return false }
|
|
298
301
|
return wrapHandles(urlScheme: urlScheme)
|
|
299
302
|
}
|
|
300
|
-
|
|
303
|
+
}
|
|
@@ -8,7 +8,11 @@ struct SpatialWebView: UIViewRepresentable {
|
|
|
8
8
|
|
|
9
9
|
func makeUIView(context: Context) -> WKWebView {
|
|
10
10
|
webviewStateChangeInvoke?(.didMakeView)
|
|
11
|
-
|
|
11
|
+
let controller = model?.getController()
|
|
12
|
+
if controller?.webview == nil {
|
|
13
|
+
model?.load()
|
|
14
|
+
}
|
|
15
|
+
return controller?.webview ?? WKWebView(frame: .zero, configuration: WKWebViewConfiguration())
|
|
12
16
|
}
|
|
13
17
|
|
|
14
18
|
func makeCoordinator() -> SpatialWebController {
|