I have a UIView which is my VC\'s view(self.view) inside which I have another UIView added as subview both looks like a square. I would like to limit the movement of subview
You need to check the "new frame" of the view you are moving, and compare its edges to the bounds of its superview:
@implementation DraggableView
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// restrict to superview bounds
CGRect parentFrame = [self.superview bounds];
UITouch *aTouch = [touches anyObject];
CGPoint location = [aTouch locationInView:self];
CGPoint previousLocation = [aTouch previousLocationInView:self];
// new frame for this "draggable" subview, based on touch offset when moving
CGRect newFrame = CGRectOffset(self.frame, (location.x - previousLocation.x), (location.y - previousLocation.y));
if (newFrame.origin.x < 0) {
// if the new left edge would be outside the superview (dragging left),
// set the new origin.x to Zero
newFrame.origin.x = 0;
} else if (newFrame.origin.x + newFrame.size.width > parentFrame.size.width) {
// if the right edge would be outside the superview (dragging right),
// set the new origin.x to the width of the superview - the width of this view
newFrame.origin.x = parentFrame.size.width - self.frame.size.width;
}
if (newFrame.origin.y < 0) {
// if the new top edge would be outside the superview (dragging up),
// set the new origin.y to Zero
newFrame.origin.y = 0;
} else if (newFrame.origin.y + newFrame.size.height > parentFrame.size.height) {
// if the new bottom edge would be outside the superview (dragging down),
// set the new origin.y to the height of the superview - the height of this view
newFrame.origin.y = parentFrame.size.height - self.frame.size.height;
}
// update this view's frame
self.frame = newFrame;
}
@end
This also uses the offset of the touch, instead of centering the view on the touch (so it doesn't "jump to your finger" when you start dragging).
Edit for clarification: In this image, the Blue view class is DraggableView
... It cannot be dragged outside of its superView (the Red view).
Edit
Swift version, with example view controller:
class DraggableView: UIView {
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// unwrap superview and touch
guard let sv = superview,
let touch = touches.first
else { return }
let parentFrame = sv.bounds
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
// new frame for this "draggable" subview, based on touch offset when moving
var newFrame = self.frame.offsetBy(dx: location.x - previousLocation.x, dy: location.y - previousLocation.y)
// make sure Left edge is not past Left edge of superview
newFrame.origin.x = max(newFrame.origin.x, 0.0)
// make sure Right edge is not past Right edge of superview
newFrame.origin.x = min(newFrame.origin.x, parentFrame.size.width - newFrame.size.width)
// make sure Top edge is not past Top edge of superview
newFrame.origin.y = max(newFrame.origin.y, 0.0)
// make sure Bottom edge is not past Bottom edge of superview
newFrame.origin.y = min(newFrame.origin.y, parentFrame.size.height - newFrame.size.height)
self.frame = newFrame
}
}
class DraggableTestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let dragView = DraggableView()
dragView.backgroundColor = .cyan
dragView.frame = CGRect(x: 20, y: 20, width: 50, height: 50)
let containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
containerView.backgroundColor = .red
containerView.addSubview(dragView)
view.addSubview(containerView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
containerView.widthAnchor.constraint(equalToConstant: 300.0),
containerView.heightAnchor.constraint(equalTo: containerView.widthAnchor),
containerView.centerXAnchor.constraint(equalTo: g.centerXAnchor),
containerView.centerYAnchor.constraint(equalTo: g.centerYAnchor),
])
}
}