I have an quite complex SwiftUI App which incoperates in on part the SceneKit Library.
Besides a lot of other functionalities (e.g. with one finger i drag and rotate objects in the scene), I want the user to control the camera via multi-touch gestures.
- two-finger pinch: Zoom the camera in and out
- two-finger pan: move the camera on x and y
But SwiftUI only has the MagnificationGesture, which I right now using as pinch for the zoom. I can't make it work to have the pan with two fingers.
I tried a lot of different implementation of SceneKit into the SwiftUI-World. Right now I have a working solution with a quite good manipulability of the scene. But gestures are a big problem. I was not able to have prober implementation of the UIKit-Gestures, however it may be possbile?
Since I need full control over the scene and the gestures, i cannot use "allow Camera Control".
Has anyone out here a solution or a hint for me?
Thanks!
Here snippets out of my code
View:
import Combine
import SwiftUI
import SceneKit
struct Canvas3DView: View {
@StateObject private var canvas3DViewModel: Canvas3DViewModel
@State private var dragOver = false
@State private var dragPosition: CGPoint = .zero
private let topBarHeight : CGFloat = 20
var body: some View {
ZStack { // 3D Canvas and PopUp Menus
ZStack { // 3DCanvas with direct 2D HUD
// 3D Canvas
SceneView(
scene: Canvas3DViewModel.scene,
pointOfView: canvas3DViewModel.pointOfViewNode,
delegate: canvas3DViewModel.sceneRendererDelegate,
technique: canvas3DViewModel.getHightlighTechnique()
)
HStack{
Spacer()
if(self.canvas3DViewModel.isDebugModeOn) {
DebugView(c3VM: self.canvas3DViewModel)
}
}
// 2D HUD
VStack {
topBar
.padding([.bottom, .top], 5)
.background(Color.white.opacity(0.25))
SpawnArea(c3VM: self.canvas3DViewModel)
Spacer()
.padding([.leading, .trailing], 8)
bottomBar
.padding([.bottom, .top], 5)
}
.font(.system(size: topBarHeight))
FlexibleSheet(sheetMode: self.$canvas3DViewModel.showingTable, definedOffset: 80) {
SomeOtherView(canvas3DVM: self.canvas3DViewModel)
}
}
.gesture(self.canvas3DViewModel.exclusiveGesture)
.onTapGesture { location in
_ = self.canvas3DViewModel.pick(atPoint: location, isTapAction: true)
dragPosition = location
}
.defersSystemGestures(on: .all)
.ignoresSafeArea()
.onDisappear(perform: {self.canvas3DViewModel.sceneRendererDelegate.onEachFrame = nil})
.onAppear(perform: {self.canvas3DViewModel.sceneRendererDelegate.onEachFrame = self.canvas3DViewModel.onEachFrame})
.coordinateSpace(name: Canvas3DViewModel.DRAGCOORDINATESPACE)
.cornerRadius(15)
.overlay(
RoundedRectangle(cornerRadius: 15)
.stroke(Color.systemBlue, lineWidth: 2)
)
.toast(message: self.canvas3DViewModel.toastMessage, isShowing: self.$canvas3DViewModel.showToast, duration: Toast.long)
.padding()
.navigationTitle("3D Scene " + (self.canvas3DViewModel.isDebugModeOn ? "!DebugMode!" : ""))
}
}
implementation gesture:
public var magnify: some Gesture {
MagnificationGesture()
.onChanged{ [self] value in
// preparing
if(self.firstMagnify) {
self.firstMagnify = false
self.lastMagnifyValue = value
}
// execution
currentPinchValue = pinchRange.clamp(value: currentPinchValue - value)
self.pointOfViewNode.camera!.orthographicScale = currentPinchValue
// post-work
self.lastMagnifyValue = value
}
.onEnded{ value in
self.firstMagnify = true
self.lastMagnifyValue = 0
}
}
public var exclusiveGesture: some Gesture {
ExclusiveGesture(self.drag, self.magnify)
}