1

When loading a model as a Model3D object, you can add a gesture modifier like so:

 Model3D(named: model_name, bundle: realityKitContentBundle){ model in
             model
             .resizable()
             .aspectRatio(contentMode: .fit)
} ...
.gesture(DragGesture()
.onChanged { 
...
}

Another example is shown on this post.

How does somebody add a gesture such as DragGesture() to a loaded entity? The entity likely needs InputTarget and Collision components added to work:

let loadedEntity = try await ModelEntity(named: modelName, in: RealityKitContent.realityKitContentBundle)
                
// Add components to entity
loadedEntity.generateCollisionShapes(recursive: true)
loadedEntity.components.set(InputTargetComponent())
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
Zach
  • 1
  • 1
  • 9

2 Answers2

1

Rotating 3D view with a Drag Gesture

If a 3D model is alone in SwiftUI view, you can rotate the whole view with a drag gesture. However, there may be several models in a scene. So, if you prefer to rotate each model of the scene separately from the others, read this post.

When you use RealityView, you get a unique functionality that is not available in the Model3D view (I mean the ability to programmatically change a scene's structure, or add anchors and components). Here's my code:

enter image description here

import SwiftUI
import RealityKit

struct ContentView: View {

    @State var isDragging: Bool = false
    @State var rotation: Angle = .zero
    
    var drag: some Gesture {
        DragGesture()
            .onChanged { _ in
                isDragging = true
                rotation.degrees += 5.0
            }
            .onEnded { _ in
                isDragging = false
            }
    }   
    var body: some View {
        RealityView { content in
            let cube = ModelEntity(mesh: .generateBox(size: 0.25))
            cube.generateCollisionShapes(recursive: false)
            cube.components.set(InputTargetComponent())
            content.add(cube)
        }
        .gesture(drag)
        .rotation3DEffect(rotation, axis: .xy)
    }
}
#Preview {
    ContentView()
}
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • 1
    Are you saying that if multiple objects need to be added to a scene with independent rotation gestures, Model3D should be used instead of RealityView? – Zach Jul 31 '23 at 18:42
  • Nope. Using Model3D views, if you need to rotate 2 models independently, you must use two Model3D views with `isDraggingA` and `isDraggingB`, `rotationA` and `rotationB`, `dragA` and `dragB` properties. But it's inconveniently. – Andy Jazz Aug 02 '23 at 09:37
  • The most convenient way is to use RealityView in which 2 (or more) models are part of the hierarchical structure of this RealityView. – Andy Jazz Aug 02 '23 at 09:41
  • This seems odd as Apple would want for complex scenes (one RealityView) where multiple objects have independent interactions. Yet, the examples I am seeing everywhere, including in documentation, point to rotating the entire view instead of single entities within the view. – Zach Aug 02 '23 at 19:59
  • 1
    I posted another question where we can dive further into this topic https://stackoverflow.com/questions/76823188/rotate-multiple-entities-independently – Zach Aug 02 '23 at 20:08
0

I was able to accomplish the rotation by attaching a gesture to the RealityView and accessing the child entity through its anchor. However, I find this to not be the best solution under the idea that there are multiple objects within the same RealityView.

There must be a better way to attach gestures object-by-object.

RealityView { content in
    do {
        ...
    } catch {
        print("oopsie")
    }
}.gesture(
    DragGesture()
        .onChanged { value in
            // Calculate rotation angle
            let angle = sqrt(pow(value.translation.width, 2) + pow(value.translation.height, 2))
            rotation = Angle(degrees: Double(angle)) 
            // Calculate rotation axis
            let axisX = -value.translation.height / CGFloat(angle)
            let axisY = value.translation.width / CGFloat(angle)
            rotationAxis = (x: axisX, y: axisY, z: 0)
            // Apply rotation to loadedEntity
            let quaternion = simd_quatf(
                angle: Float(rotation.radians),
                axis: SIMD3<Float>(
                    x: Float(rotationAxis.x),
                    y: Float(rotationAxis.y),
                    z: Float(rotationAxis.z)
                )
            )
            anchor.children[0].orientation = quaternion
        }
)
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Zach
  • 1
  • 1
  • 9