Reputation: 69
Let's say that I have:
UIImageView
called fox
fence
UIView
embedded in ViewControllers
by defaultNow in other words, I want the fox
to move only inside it's fence
In my viewDidLoad()
:
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(fence)
fence.addSubview(fox)
}
Now this part works fine, I figured to move the fox
by subclassing UIImageView
with a little bit of modifications:
class DraggableImageView: UIImageView {
var dragStartPositionRelativeToCenter : CGPoint?
override init(image: UIImage!) {
super.init(image: image)
self.isUserInteractionEnabled = true
addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan(nizer:))))
layer.shadowColor = UIColor.black.cgColor
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowOpacity = 0.5
layer.shadowRadius = 2
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc func handlePan(nizer: UIPanGestureRecognizer!) {
if nizer.state == UIGestureRecognizer.State.began {
let locationInView = nizer.location(in: superview)
dragStartPositionRelativeToCenter = CGPoint(x: locationInView.x - center.x, y: locationInView.y - center.y)
layer.shadowOffset = CGSize(width: 0, height: 20)
layer.shadowOpacity = 0.3
layer.shadowRadius = 6
return
}
if nizer.state == UIGestureRecognizer.State.ended {
dragStartPositionRelativeToCenter = nil
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowOpacity = 0.5
layer.shadowRadius = 2
return
}
let locationInView = nizer.location(in: superview)
UIView.animate(withDuration: 0.1) {
self.center = CGPoint(x: locationInView.x - self.dragStartPositionRelativeToCenter!.x,
y: locationInView.y - self.dragStartPositionRelativeToCenter!.y)
}
}}
Now I can drag the fox
object anywhere i like, but; what if I wanted to only move the fox
inside the fence
object?, since it's a subview, I think it's possible.
Upvotes: 1
Views: 62
Reputation: 127
In order to keep the image view inside its parent, I added a check before you update the center of the view that makes sure that the views frame will be in the parents frame. The center is only updated if the update would keep the view within its parent's frame.
I also updated the pan handler to use the translation (similar to the example in the pan gesture documentation) as opposed to the locationInView. This makes the drag behave better.
I've tested this and I believe it behaves in the way you desire. Hope this helps:
@objc func handlePan(nizer: UIPanGestureRecognizer!) {
if nizer.state == UIGestureRecognizer.State.began {
dragStartPositionRelativeToCenter = self.center
layer.shadowOffset = CGSize(width: 0, height: 20)
layer.shadowOpacity = 0.3
layer.shadowRadius = 6
return
}
if nizer.state == UIGestureRecognizer.State.ended {
dragStartPositionRelativeToCenter = nil
layer.shadowOffset = CGSize(width: 0, height: 3)
layer.shadowOpacity = 0.5
layer.shadowRadius = 2
return
}
if let initialCenter = dragStartPositionRelativeToCenter {
let translation = nizer.translation(in: self.superview)
let newCenter = CGPoint(x: initialCenter.x + translation.x, y: initialCenter.y + translation.y)
if frameContainsFrameFromCenter(containingFrame: superview!.frame, containedFrame: self.frame, center: newCenter) {
UIView.animate(withDuration: 0.1) {
self.center = newCenter
}
}
}
}
func frameContainsFrameFromCenter(containingFrame: CGRect, containedFrame: CGRect, center: CGPoint) -> Bool {
let leftMargin = containedFrame.width / 2
let topMargin = containedFrame.height / 2
let testFrame = CGRect(
x: leftMargin,
y: topMargin,
width: containingFrame.width - (2*leftMargin),
height: containingFrame.height - (2*topMargin)
)
return testFrame.contains(center)
}
Upvotes: 3