1

I can programmatically create a 3D cube in iOS using the code below. However, I need to use gestures to define its size. With a pinch gesture I'd like to build a base of cube in XZ axis. And with a drag gesture I'd like to build a height of cube in Y axis.

The question is: How to construct a 3D cube using these two gestures?

enter image description here

Here's my code:

import UIKit
import SceneKit

class GameViewController: UIViewController {
    var scnView: SCNView!
    var scnScene: SCNScene!
    var cameraNode: SCNNode!

    override func viewDidLoad() {
        super.viewDidLoad()
        self.setupView()
        self.setupScene()
        self.setupCamera()
        self.spawnShape()
    }
    override func shouldAutorotate() -> Bool {
        return true
    }
    override func prefersStatusBarHidden() -> Bool {
        return true
    }
    func setupView() {
        scnView = self.view as! SCNView
        scnView.allowsCameraControl = true
        scnView.autoenablesDefaultLighting = true
    }
    func setupScene() {
        scnScene = SCNScene()
        scnView.scene = scnScene
        scnScene.background.contents = UIImage(named: "bgTexture.png")
    }  
    func setupCamera() {
        cameraNode = SCNNode()
        cameraNode.camera = SCNCamera()
        cameraNode.position = SCNVector3(x: 0, y: 0, z: 12)
        scnScene.rootNode.addChildNode(cameraNode)
    }    
    func spawnShape() {
        var geometry: SCNGeometry = SCNBox(width: 1.0, 
                                          height: 1.0, 
                                          length: 1.0, 
                                   chamferRadius: 0.1)
        let geometryNode = SCNNode(geometry: geometry)
        scnScene.rootNode.addChildNode(geometryNode)
    }
}   

Here's how I tried to implement UIPinchGestureRecognizer() and UIPanGestureRecognizer().

var pinchGesture = UIPinchGestureRecognizer()
var dragGesture = UIPanGestureRecognizer()

override func viewDidLoad() {
    super.viewDidLoad()
    setupView()
    setupScene()
    setupCamera()
    spawnShape()

    self.scnView.userInteractionEnabled = true
    self.scnView.multipleTouchEnabled = true

    pinchGesture = UIPinchGestureRecognizer(target: self, 
                                            action: #selector(pinchRecognized))
    dragGesture = UIPanGestureRecognizer(target: self, 
                                         action: #selector(panRecognized))
    self.scnView.addGestureRecognizer(self.pinchGesture)
    self.scnView.addGestureRecognizer(self.dragGesture)
}

@IBAction func pinchRecognized(pinch: UIPinchGestureRecognizer) { ... }
@IBAction func panRecognized(pan: UIPanGestureRecognizer) { ... }
Andy Jazz
  • 49,178
  • 17
  • 136
  • 220
  • Have you tried anything yet? That sounds like a very non-trivial task if you try to implement it on your own. – luk2302 Jan 21 '17 at 11:25
  • So post the code you tried and explain how it didn't work. – Duncan C Jan 21 '17 at 12:20
  • Can't you use set your width and length scale to the pinchgesture scale and reset the pinchgesturescale to 1.0 every time it is called? This will enable incremental growth. – Emptyless Jan 21 '17 at 13:16
  • 1
    unrelated to code/implementation; a thought on UX: I've used a lot of 3D design apps, over decades, including the new iOS apps for iPad Pro etc. Rather than pinch, for the initial plane creation, I think a single touch and drag operation would be vastly better. The user can start from any corner, and drag in the direction desired to create the base plane. Having done so, to their satisfaction, let them either add a second finger and drag up or down to extrude the plane into a 3D cube, or release and commit a second drag with one finger to do the same. – Confused Jan 23 '17 at 08:45

1 Answers1

3

You can use UIGestureRecognizers to accomplish what you are looking for.

in your ViewDidLoad() add your recognizer:

    // Add Gesture recognizers
    let pinch = UIPinchGestureRecognizer(target: self, action: #selector(scaleSizeOfGeometry(sender:)))
    scnView.addGestureRecognizer(pinch)

Incremental scaling in the XZ directions can now be accomplished by using:

func scaleSizeOfGeometry(sender: UIPinchGestureRecognizer) {
    let scale = Float(sender.scale)
    referenceToGeometry?.scale.x *= scale
    referenceToGeometry?.scale.z *= scale
    sender.scale = 1.0
}

You have to reference the geometry you want to scale somehow. For example with a variable referenceToGeometry : SCNNode? that is set in your spawnGeometry():

referenceToGeometry = geometryNode

The UIPanGestureRecognizer is a bit more tricky since you can use a velocity or a translation to scale in the Y direction. In your viewDidLoad() add the gesture recognizer:

let pan = UIPanGestureRecognizer(target: self, action: #selector(scaleHeightOfGeometry(sender:)))
scnView.addGestureRecognizer(pan)

And in your function use either the velocity or the translation:

func scaleHeightOfGeometry(sender: UIPanGestureRecognizer) {
    // y : sender.translation(in: self.scnView).y
    // v : sender.velocity(in: self.scnView).y
}

You will have to extract a multiplier to your own liking from either of these. For example by storing the y[i-1] and checking the difference between the two, than adding the difference to the height of your geometry and storing the newValue as the y[i-1]th value.

EDIT:

To add to the post, you can use UIGestureRecognizer states for more advanced/finetuned versions of this.

Emptyless
  • 2,964
  • 3
  • 20
  • 30