限制子视图在父视图边界内的移动 [英] Restrict the movement of subview inside superview bounds

查看:36
本文介绍了限制子视图在父视图边界内的移动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个 UIView,它是我的 VC 的视图(self.view),其中我添加了另一个 UIView 作为子视图,它们看起来都像一个正方形.我想限制子视图在父视图中的移动.基本上子视图不应该超出父视图的边界.
我确实实施了

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {//我检查触摸点是否在我的超级视图内我设置了中心,即UITouch *touch = [[UITouch allObjects] firstObject];if (CGRectContainsPoint(subview.frame, [touch locationInView:self.view])) {//在这里设置中心...即subview.center = [touch locationInView:self.view];}

这有效并且子视图正在移动,但它也会移动到父视图之外;如何仅限制超级视图内的移动?

我什至尝试过类似的东西.

if (CGRectContainsRect(self.view.bounds, subview.frame)) {//这个条件有效,但我不知道为什么我的子视图如果我试图将它拖过超级视图的边界,它会粘在超级视图的两侧}

我在 touchesBegan、touchesCancelled 和 touchesEnd 中没有做任何事情.

解决方案

您需要检查新框架"您正在移动的视图,并将其边缘与其父视图的边界进行比较:

@implementation DraggableView- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{//限制到超视图边界CGRect parentFrame = [self.superview bounds];UITouch *aTouch = [touches anyObject];CGPoint location = [aTouch locationInView:self];CGPoint previousLocation = [aTouch previousLocationInView:self];//这个可拖动"的新框架子视图,基于移动时的触摸偏移CGRect newFrame = CGRectOffset(self.frame, (location.x - previousLocation.x), (location.y - previousLocation.y));如果(newFrame.origin.x <0){//如果新的左边缘将在超级视图之外(向左拖动),//将新的 origin.x 设置为零newFrame.origin.x = 0;} else if (newFrame.origin.x + newFrame.size.width > parentFrame.size.width) {//如果右边缘在父视图之外(向右拖动),//设置新的origin.x为superview的宽度——这个view的宽度newFrame.origin.x = parentFrame.size.width - self.frame.size.width;}如果(newFrame.origin.y <0){//如果新的顶部边缘将在超级视图之外(向上拖动),//将新的 origin.y 设置为零newFrame.origin.y = 0;} else if (newFrame.origin.y + newFrame.size.height > parentFrame.size.height) {//如果新的底边将在超级视图之外(向下拖动),//设置新的origin.y为superview的高度——这个view的高度newFrame.origin.y = parentFrame.size.height - self.frame.size.height;}//更新这个视图的框架self.frame = newFrame;}@结尾

这也使用了触摸的偏移量,而不是使视图以触摸为中心(因此当您开始拖动时它不会跳到您的手指").

编辑澄清:在这张图片中,蓝色视图类是DraggableView...它不能被拖到它的超级视图(红色视图)之外.>


编辑

Swift 版本,带有示例视图控制器:

class DraggableView: UIView {覆盖 func touchesMoved(_ touches: Set, with event: UIEvent?) {//展开超级视图并触摸守卫让 sv = 超级视图,让触摸 = touches.first否则{返回}让 parentFrame = sv.bounds让位置 = touch.location(in: self)让 previousLocation = touch.previousLocation(in: self)//这个可拖动"的新框架子视图,基于移动时的触摸偏移var newFrame = self.frame.offsetBy(dx: location.x - previousLocation.x, dy: location.y - previousLocation.y)//确保左边缘不超过父视图的左边缘newFrame.origin.x = max(newFrame.origin.x, 0.0)//确保右边缘不超过父视图的右边缘newFrame.origin.x = min(newFrame.origin.x, parentFrame.size.width - newFrame.size.width)//确保顶边不超过父视图的顶边newFrame.origin.y = max(newFrame.origin.y, 0.0)//确保底部边缘不超过超级视图的底部边缘newFrame.origin.y = min(newFrame.origin.y, parentFrame.size.height - newFrame.size.height)self.frame = newFrame}}类 DraggableTestViewController: UIViewController {覆盖 func viewDidLoad() {super.viewDidLoad()让dragView = DraggableView()dragView.backgroundColor = .cyandragView.frame = CGRect(x: 20, y: 20, width: 50, height: 50)让容器视图 = UIView()containerView.translatesAutoresizingMaskIntoConstraints = falsecontainerView.backgroundColor = .redcontainerView.addSubview(dragView)view.addSubview(containerView)让 g = view.safeAreaLayoutGuideNSLayoutConstraint.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),])}}

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.

解决方案

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

    }
    
}

这篇关于限制子视图在父视图边界内的移动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆