Swift dragging a UIView with snap
Swift dragging a UIView
Here is one simple way to drag a UIView.
Introduction
There are many ways to drag a UIView around. In my example on Github, I drag a custom UIView subclass that does nothing special besides drawing itself. In real life you’d probably have additional code in it. (One hint at that is in my example view, I fill the rectangle with a color instead of simply setting its backgroundColor property).
I could have put the event handling in the custom UIView.
Something like this:
1 2 |
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) { etc |
Nah. Here I’m going to use a UIPanGestureRecognizer. In the ViewController, I’ll install the recognizer on the “parent” view.
1 2 3 4 |
var pan = UIPanGestureRecognizer(target:self, action:"pan:") pan.maximumNumberOfTouches = 1 pan.minimumNumberOfTouches = 1 self.view.addGestureRecognizer(pan) |
Beginning the drag
In the recognizer action, I first grab the location of the event. Then, depending on the state of the recognizer, I implement the different parts of the drag functionality.
First, you need to get the subview that you clicked upon. I do this in the .Began state.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
func pan(rec:UIPanGestureRecognizer) { var p:CGPoint = rec.locationInView(self.view) var center:CGPoint = CGPointZero switch rec.state { case .Began: println("began") self.selectedView = view.hitTest(p, withEvent: nil) if self.selectedView != nil { self.view.bringSubviewToFront(self.selectedView!) } etc. |
Dragging
The actual dragging takes place in the .Changed state. If there is a view selected, I store it’s center property. Then, I calculate how far the touch moved. You can use this as a threshold. Since I want to be able to configure whether the view can be dragged in the x or y direction (or both), I use two instance variables shouldDragX and shouldDragY. If these are true I set the center property of the selected view to the new location. This location has been “snapped” by a snap value. For example, if snapX is 25.0, the view will be dragged only in increments of 25.0.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
case .Changed: if let subview = selectedView { center = subview.center var distance = sqrt(pow((center.x - p.x), 2.0) + pow((center.y - p.y), 2.0)) println("distance \(distance)") if subview is MyView { if distance > threshold { if shouldDragX { subview.center.x = p.x - (p.x % snapX) } if shouldDragY { subview.center.y = p.y - (p.y % snapY) } } } } etc. |
Ending the drag
Then, in the .Ended state, I set the selectedView to nil to start over. You can also do whatever processing you need here.
1 2 3 4 5 6 7 8 |
case .Ended: if let subview = selectedView { if subview is MyView { // do whatever } } // must do this of course selectedView = nil |
Summary
Install a UIPanGestureRecognizer on a parent view to drag a subview. It goes without saying that the parent view should do no layout on its subviews, nor should they have any constraints.
This is not “drag and drop”, because there is no data transfer. You are simply rearranging the location of a UIView.
One Reply to “Swift dragging a UIView with snap”