Search code examples
iosswiftcollision-detectiondrag

Move many views against one another after collision iOS?


I have been unsuccesfully trying to implement the following in my iOS application for the last few days

(Source)

WHAT I WANT

When any of these circles are dragged by the user (whether red or grey), the circles that come in contact with the moving one should move . So if all the circles were in a line and the first was moved to the right , it would push the rest of the circles to the right.

WHAT I'VE TRIED SO FAR

1) intersects(_ rect2: CGRect) method. I couldn't think of anything else other than creating a huge number of nested for loops with the intersects method for every circle. Firstly , it looks very cluttery and Secondly the method is inefficient i.e. the method doesn't respond when the user drags too quickly .

2) UICollisionBehaviour . After reading in another question , I discoverd that this somehow only works when UIDynamicAnimator is responsible for moving Views on screen , NOT the user.(as with drag)

Finally I think , Sprite Kit is what I need , but I am not sure how or why to use it for such a tiny component of my app.

Any suggestion would be aspirin. Thanks!


Solution

  • I was able to perform the move against collision effect using Sprite kit . It wasn't hard , as I had imagined.

    This is however simply a proof of concept and doesn't address issues like being receptive to super fast movement.

    If you are new to SpriteKit , go through this article from RayWenderlich.com . Ofcourse Apple's Programming guide to Sprite Kit is another amazing resource.

    CODE

       class GameScene : SKScene
    
       var orangeBall : SKShapeNode!
       var greenBall  : SKShapeNode!
    
     // Description :  - Create two circular shapes here 
     //                - Give them physics bodies 
     //                - Add them  
    
      override func didMove(to view: SKView) {
    
    
    
    
        orangeBall = SKShapeNode(circleOfRadius: 50 )                       // Size of Circle
        orangeBall.position = CGPoint(x: frame.midX, y: frame.midY)         // Middle of Screen
        orangeBall.glowWidth = 1.0
        orangeBall.fillColor = SKColor.orange
    
        orangeBall.physicsBody = SKPhysicsBody(circleOfRadius: 50 )
        orangeBall.physicsBody?.affectedByGravity = false
        orangeBall.physicsBody?.isDynamic = true
        orangeBall.physicsBody?.allowsRotation = false
        orangeBall.physicsBody?.restitution = 1
    
        self.addChild(orangeBall)
    
    
    
        greenBall = SKShapeNode(circleOfRadius: 50 )                       // Size of Circle
        greenBall.position = CGPoint(x: frame.midX + 70, y: frame.midY + 55.0 )         // Middle of Screen
        greenBall.glowWidth = 1.0
        greenBall.fillColor = SKColor.green
    
        greenBall.physicsBody = SKPhysicsBody(circleOfRadius: 50)
        greenBall.physicsBody?.affectedByGravity = false
        greenBall.physicsBody?.isDynamic = true
        greenBall.physicsBody?.density = greenBall.physicsBody!.density * 0.000001
        greenBall.physicsBody?.allowsRotation = false
        greenBall.physicsBody?.restitution = 1
    
        self.addChild(greenBall)
    
    }
    

    Configuring touch

    var fingerOnOrangeBall = false
    var fingerOnGreenBall  = false
    
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    
                    if orangeBall.contains(touches.first!.location(in: self)){ fingerOnOrangeBall =  true }
    
                    if greenBall.contains( touches.first!.location(in: self)){ fingerOnGreenBall =   true }
    
    
    
      }
    
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
    
        if fingerOnOrangeBall{
    
            let touch =      touches.first
            var position =   touch!.location(in: self.view)
            position =       self.convertPoint(fromView: position)
            orangeBall.position = position
    
        }
    
        if fingerOnGreenBall{
    
            let touch =      touches.first
            var position =   touch!.location(in: self.view)
            position =       self.convertPoint(fromView: position)
            greenBall.position = position
    
        }
    
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    
                       if   fingerOnOrangeBall { fingerOnOrangeBall =  false  }
    
                       if   fingerOnGreenBall {  fingerOnGreenBall =  false  }
    
    }
     }