Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
618 views
in Technique[技术] by (71.8m points)

ios - Restrict the movement of subview inside superview bounds

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 inside the superview. Basically the subview should not go outside the superview bounds.
I did implement

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event { 
// i check if point of touch is inside my superview i set the center i.e
UITouch *touch = [[UITouch allObjects] firstObject];
if (CGRectContainsPoint(subview.frame, [touch locationInView:self.view])) {
// set the center here... i.e
subview.center = [touch locationInView:self.view];
}

This works and subview is moving but it also moves outside the superview; how can I restrict the movement within the superview only?

I even tried something like .

if (CGRectContainsRect(self.view.bounds, subview.frame)) {
// this condition works but i don't know why my subview sticks to the sides of superview if i try to drag it through the bounds of superview
}

I don't do anything in touchesBegan, touchesCancelled, and nothing relevant in touchesEnd as well.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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).

enter image description here


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),
        ])

    }
    
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...