I can move a single UIView
using the code below but how do I move multiple UIView
's individually using IBOutletCollection
and tag
values?
class TeamSelection: UIViewController {
var location = CGPoint(x: 0, y: 0)
@IBOutlet weak var ViewTest: UIView! // move a single image
@IBOutlet var Player: [UIView]! // collection to enable different images with only one outlet
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch: UITouch = touches.first! as UITouch
location = touch.location(in: self.view)
ViewTest.center = location
}
}
There are two basic approaches:
You could iterate through your subviews figuring out in which one the touch intersected and move it. But this approach (nor the use of cryptic tag
numeric values to identify the view) is generally not the preferred method.
Personally, I'd put the drag logic in the subview itself:
class CustomView: UIView { // or subclass `UIImageView`, as needed
private var originalCenter: CGPoint?
private var dragStart: CGPoint?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
originalCenter = center
dragStart = touches.first!.location(in: superview)
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
var location = touch.location(in: superview)
if let predicted = event?.predictedTouches(for: touch)?.last {
location = predicted.location(in: superview)
}
center = dragStart! + location - originalCenter!
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let location = touch.location(in: superview)
center = dragStart! + location - originalCenter!
}
}
extension CGPoint {
static func +(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y)
}
static func -(lhs: CGPoint, rhs: CGPoint) -> CGPoint {
return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y)
}
}
Remember to set "user interaction enabled" for the subviews if you use this approach.
By the way, if you're dragging views like this, make sure you don't have constraints on those views or else when the auto-layout engine next applies itself, everything will move back to the original location. If using auto-layout, you'd generally modify the constant
of the constraint.
A couple of observations on that dragging logic:
You might want to use predictive touches, like above, to reduce lagginess of the drag.
Rather than moving the center
to the location(in:)
of the touch, I would rather keep track of by how much I dragged it and either move the center
accordingly or apply a corresponding translation. It's a nicer UX, IMHO, because if you grab the the corner, it lets you drag by the corner rather than having it jump the center of the view to where the touch was on screen.