Combining UIAttachmentBehavior and UICollisionBehavior

566 views Asked by At

Using UIKit Dynamics I want to combine a UIAttachmentBehavior and UICollisionBehavior in a way such that the user can drag a view around (using a UIPanGestureRecognizer) but not leave a certain area.

The problem arises, when the user/UIView collide with the bounds of the collision behavior, as no perpendicular movement is then possible.

I.E. when colliding with the left side of the bounds area one is "stuck" there and can't move up or down, just right. Dragging the UIView back on the exact path used to go there works.

Any help on which UIDynamicItemBehavior works around this is highly appreciated (tried elasticity, friction and resistance, but to no avail).

enter image description here

1

There are 1 answers

0
aytek On

I think you implemented UIPanGestureRecognizer in a wrong way. Try the following example. Just create a new project and paste this code into your ViewController. If you need to reduce the draggable area, you can play with the dragView function.

import UIKit

class ViewController: UIViewController, UICollisionBehaviorDelegate {

    var collision: UICollisionBehavior!
    var animator: UIDynamicAnimator!

    override func viewDidLoad() {
        super.viewDidLoad()

        let container = UIView(frame: CGRect(x: 30, y: 60, width: 300, height: 500))
        self.view.addSubview(container)
        container.backgroundColor = .gray

        self.animator = UIDynamicAnimator(referenceView: container);
        //self.animator.setValue(true, forKey: "debugEnabled")

        let greenBox = UIView(frame: CGRect(x: 60, y: 240, width: 50, height: 50))
        greenBox.backgroundColor = .green
        container.addSubview(greenBox)

        let blueBox = UIView(frame: CGRect(x: 10, y: 10, width: 50, height: 50))
        blueBox.backgroundColor = .blue
        container.addSubview(blueBox)

        let redBox = UIView(frame: CGRect(x: 200, y: 300, width: 50, height: 50))
        redBox.backgroundColor = .red
        container.addSubview(redBox)

        self.collision = UICollisionBehavior(items: [greenBox, blueBox, redBox])
        self.collision.translatesReferenceBoundsIntoBoundary = true
        self.collision.collisionMode = .everything
        self.animator.addBehavior(self.collision)
        self.collision.collisionDelegate = self


        let c = UIDynamicItemBehavior(items: [redBox])
        c.density = 10000
        self.animator.addBehavior(c)

        let noRotation = UIDynamicItemBehavior(items: [greenBox, blueBox])
        noRotation.allowsRotation = false
        self.animator.addBehavior(noRotation)

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(self.dragView(_:)))
        greenBox.isUserInteractionEnabled = true
        greenBox.addGestureRecognizer(panGesture)

        let panGesture2 = UIPanGestureRecognizer(target: self, action: #selector(self.dragView(_:)))
        blueBox.isUserInteractionEnabled = true
        blueBox.addGestureRecognizer(panGesture2)

    }


    @objc func dragView(_ sender:UIPanGestureRecognizer){

        if let viewDrag = sender.view {
            self.view.bringSubview(toFront: viewDrag)
            let translation = sender.translation(in: self.view)
            viewDrag.center = CGPoint(x: viewDrag.center.x + translation.x, y: viewDrag.center.y + translation.y)
            sender.setTranslation(CGPoint.zero, in: self.view)
            animator.updateItem(usingCurrentState: viewDrag)
        }
    }

}