Dpetrov
Dpetrov

Reputation: 546

Draggable UIView Swift 3

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

Upvotes: 32

Views: 34595

Answers (4)

oblomov
oblomov

Reputation: 28

This UIView extension makes a UIView object draggable and limits the movement to stay within the bounds of the screen.

    extension UIView {
    func makeDraggable() {
        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        self.addGestureRecognizer(panGesture)
    }
    
    @objc func handlePan(_ gesture: UIPanGestureRecognizer) {
        guard gesture.view != nil else { return }
        
        let translation = gesture.translation(in: gesture.view?.superview)
        
        var newX = gesture.view!.center.x + translation.x
        var newY = gesture.view!.center.y + translation.y
        
        let halfWidth = gesture.view!.bounds.width / 2.0
        let halfHeight = gesture.view!.bounds.height / 2.0
        
        // Limit the movement to stay within the bounds of the screen
        newX = max(halfWidth, newX)
        newX = min(UIScreen.main.bounds.width - halfWidth, newX)
        newY = max(halfHeight, newY)
        newY = min(UIScreen.main.bounds.height - halfHeight, newY)
        
        gesture.view?.center = CGPoint(x: newX, y: newY)
        gesture.setTranslation(CGPoint.zero, in: gesture.view?.superview)
    }
}

Upvotes: 0

Fattie
Fattie

Reputation: 12590

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() { }
}

Upvotes: 8

Dilip Tiwari
Dilip Tiwari

Reputation: 1451

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.

Upvotes: 6

Kirit Modi
Kirit Modi

Reputation: 23407

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

Upvotes: 122

Related Questions