Draggable UIView Swift 3

28,162

Solution 1

Step 1 : Take one View which you want to drag in storyBoard.

@IBOutlet weak var viewDrag: UIView!

Step 2 : Add PanGesture.

var panGesture       = UIPanGestureRecognizer()

Step 3 : In ViewDidLoad adding the below code.

override func viewDidLoad() {
    super.viewDidLoad()

    panGesture = UIPanGestureRecognizer(target: self, action: #selector(ViewController.draggedView(_:)))
    viewDrag.isUserInteractionEnabled = true
    viewDrag.addGestureRecognizer(panGesture)

}

Step 4 : Code for draggedView.

func draggedView(_ sender:UIPanGestureRecognizer){
    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)
}

Step 5 : Output.

GIF

Solution 2

Use below code for Swift 5.0

Step 1 : Take one UIView from Storyboard, drag it into your ViewController file and Create IBOutlet of UIView.

@IBOutlet weak var viewDrag: UIView!
var panGesture = UIPanGestureRecognizer()

Step 2 : In viewDidLoad() adding the below code.

override func viewDidLoad() {
super.viewDidLoad()
  let panGesture = UIPanGestureRecognizer(target: self, action:(Selector(("draggedView:"))))
  viewDrag.isUserInteractionEnabled = true
  viewDrag.addGestureRecognizer(panGesture)
}

Step 3 : Create func and add code to move the UIView as like below.

func draggedView(sender:UIPanGestureRecognizer){
    self.view.bringSubviewToFront(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)
}

Hope this will help someone.

Solution 3

It's very easy if you subclass a view:

DraggableView...

class DraggableView: UIIView {
    
    var fromleft: NSLayoutConstraint!
    var fromtop: NSLayoutConstraint!
    
    override func didMoveToWindow() {
        super.didMoveToWindow()
        if window != nil {
            fromleft = constraint(id: "fromleft")!
            fromtop = constraint(id: "fromtop")!
        }
    }
    
    override func common() {
        super.common()
        let p = UIPanGestureRecognizer(
           target: self, action: #selector(drag))
        addGestureRecognizer(p)
    }
    
    @objc func drag(_ s:UIPanGestureRecognizer) {
        let t = s.translation(in: self.superview)
        fromleft.constant = fromleft.constant + t.x
        fromtop.constant = fromtop.constant + t.y
        s.setTranslation(CGPoint.zero, in: self.superview)
    }
}
  1. Drop a UIView in your scene.

  2. As normal, add a constraint from the left (that's the x position) and add a constraint from the top (that's the y position).

  3. In storyboard simply simply name the constraints "fromleft" and "fromtop"

enter image description here

You're done.

It now works perfectly - that's it.

What is that handy constraint( call ?

Notice the view simply finds its own constraints by name.

In Xcode there is STILL no way to use constraints like IBOutlets. Fortunately it is very easy to find them by "identifier". (Indeed, this is the very purpose of the .identifier feature on constraints.)

extension UIView {
    
    func constraint(id: String) -> NSLayoutConstraint? {
        let cc = self.allConstraints()
        for c in cc { if c.identifier == id { return c } }
        //print("someone forgot to label constraint \(id)") //heh!
        return nil
    }
    
    func allConstraints() -> [NSLayoutConstraint] {
        var views = [self]
        var view = self
        while let superview = view.superview {
            views.append(superview)
            view = superview
        }
        return views.flatMap({ $0.constraints }).filter { c in
            return c.firstItem as? UIView == self ||
                c.secondItem as? UIView == self
        }
    }

Tip...edge versus center!

Don't forget when you make the constraints on a view (as in the image above):

you can set the left one to be either:

  • to the left edge of the white box, or,
  • to the center of the white box.

Choose the correct one for your situation. It will make it much easier to do calculations, set sliders, etc.


Footnote - an "initializing" UIView, UI "I" View,

// UI "I" View ... "I" for initializing
// Simply saves you typing inits everywhere
import UIKit
class UIIView: UIView {
    override init(frame: CGRect) {
        super.init(frame: frame)
        common()
    }
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        common()
    }
    func common() { }
}
Share:
28,162
Dpetrov
Author by

Dpetrov

Updated on February 23, 2021

Comments

  • Dpetrov
    Dpetrov about 3 years

    I want to be able to drag the objects on the screen, but they wont. I tried everything but still cant.

    Here are the code.

    func panGesture(gesture: UIPanGestureRecognizer) {
        switch gesture.state {
        case .began:
            print("Began.")
            for i in 0..<forms.count {
                if forms[i].frame.contains(gesture.location(in: view)) {
                    gravity.removeItem(forms[i])
                }
            }
        case .changed:
            let translation = gesture.translation(in: forms[1])
    
            gesture.view!.center = CGPoint(x: gesture.view!.center.x + translation.x, y: gesture.view!.center.y + translation.y)
    
            gesture.setTranslation(CGPoint.zero, in: self.view)
    
            print("\(gesture.view!.center.x)=\(gesture.view!.center.y)")
            print("t;: \(translation)")
        case .ended:
            for i in 0..<forms.count {
                if forms[i].frame.contains(gesture.location(in: view)) {
                    gravity.addItem(forms[i])
                }
            }
            print("Ended.")
        case .cancelled:
            print("Cancelled")
        default:
            print("Default")
        }
    }
    

    Also they have gravity. The forms are squares and circles.

    Explanation: in .began - i disable the gravity for selected form. in .changed - i try to change the coordinates. in .end - i enable again gravity.

    ScreenShot.

    enter image description here

    enter image description here