Skip to content

Instantly share code, notes, and snippets.

@elkraneo
Created February 4, 2024 10:20
Show Gist options
  • Save elkraneo/5ccd1e1be8f29844008fc5a09748350b to your computer and use it in GitHub Desktop.
Save elkraneo/5ccd1e1be8f29844008fc5a09748350b to your computer and use it in GitHub Desktop.
import RealityKit
import RealityKitContent
import SwiftUI
struct ManipulationState {
var active = false
var transform: AffineTransform3D = .identity
}
struct ContentView: View {
@State private var showImmersiveSpace = false
@State private var immersiveSpaceIsShown = false
@Environment(\.openImmersiveSpace) var openImmersiveSpace
@Environment(\.dismissImmersiveSpace) var dismissImmersiveSpace
@GestureState var manipulationState = ManipulationState()
var body: some View {
VStack {
Model3D(named: "Scene", bundle: realityKitContentBundle)
.padding(.bottom, 50)
.scaleEffect(manipulationState.transform.scale.width)
.rotation3DEffect(manipulationState.transform.rotation ?? .identity)
.offset(
x: manipulationState.transform.translation.x,
y: manipulationState.transform.translation.y
)
.offset(z: manipulationState.transform.translation.z)
.animation(.spring, value: manipulationState.transform)
.gesture(
manipulationGesture.updating($manipulationState) { value, state, transaction in
state.active = true
state.transform = value
}
)
Text("Hello, world!")
Toggle("Show Immersive Space", isOn: $showImmersiveSpace)
.toggleStyle(.button)
.padding(.top, 50)
}
.padding()
.onChange(of: showImmersiveSpace) { _, newValue in
Task {
if newValue {
switch await openImmersiveSpace(id: "ImmersiveSpace") {
case .opened:
immersiveSpaceIsShown = true
case .error, .userCancelled:
fallthrough
@unknown default:
immersiveSpaceIsShown = false
showImmersiveSpace = false
}
} else if immersiveSpaceIsShown {
await dismissImmersiveSpace()
immersiveSpaceIsShown = false
}
}
}
}
}
#Preview(windowStyle: .automatic) {
ContentView()
}
//MARK: - Manipulation Gesture
// Gesture combining dragging, magnification, and 3D rotation all at once.
var manipulationGesture: some Gesture<AffineTransform3D> {
DragGesture()
.simultaneously(with: MagnifyGesture())
.simultaneously(with: RotateGesture3D())
.map { gesture in
let (translation, scale, rotation) = gesture.components()
return AffineTransform3D(
scale: scale,
rotation: rotation,
translation: translation
)
}
}
// Helper for extracting translation, magnification, and rotation.
extension SimultaneousGesture<
SimultaneousGesture<DragGesture, MagnifyGesture>,
RotateGesture3D
>.Value {
func components() -> (Vector3D, Size3D, Rotation3D) {
let translation = self.first?.first?.translation3D ?? .zero
let magnification = self.first?.second?.magnification ?? 1
let size = Size3D(width: magnification, height: magnification, depth: magnification)
let rotation = self.second?.rotation ?? .identity
return (translation, size, rotation)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment