设置平移手势的边界 [英] Setting boundaries on pan gestures

查看:46
本文介绍了设置平移手势的边界的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了 5 个 UIView,每个都带有 UIPanGestureRecognizers,然后我将它们添加到另一个 UIView.所有这些都有效,我可以拖动每一个,但我不希望它们被拖动到其父 UIView 的边界之外.我已经读过我可以使用gestureRecognizerShouldBegin 函数实现UIGestureRecognizerDelegate 来实现这一点,但是我找不到足够的文档来说明如何准确地实现我所需要的.像往常一样,Apple 对此的文档非常含糊.我的代码是:

I have created 5 UIViews, each with UIPanGestureRecognizers, which I then add to another UIView. All this works and I can drag each around, but I don't want them to be dragged outside the boundaries of their parent UIView. I have read that I can implement UIGestureRecognizerDelegate with the gestureRecognizerShouldBegin function to achieve this, but I can't find enough documentation on exactly how to achieve exactly what I need. Apple's documentation on this, as usual, is quite vague. My code is:

    let foreheadTopViewWidth = foreheadTopView.frame.width
            let foreheadTopViewHeight = foreheadTopView.frame.height
            let touchPointOffset = touchPointSize / 2
            let touchPointYPos = (foreheadTopViewHeight / 2) - touchPointOffset

            let touchPointForehead1 = TouchPoint(touchPointSize: touchPointSize, xPosition: 0, yPosition: touchPointYPos, treatmentArea: .upperForehead)
            let touchPointForehead2 = TouchPoint(touchPointSize: touchPointSize, xPosition: ((foreheadTopViewWidth * 0.50) - touchPointOffset) / 2, yPosition: touchPointYPos, treatmentArea: .upperForehead)
            let touchPointForehead3 = TouchPoint(touchPointSize: touchPointSize, xPosition: (foreheadTopViewWidth * 0.50) - touchPointOffset, yPosition: touchPointYPos, treatmentArea: .upperForehead)
            let touchPointForehead4 = TouchPoint(touchPointSize: touchPointSize, xPosition: (foreheadTopViewWidth * 0.75) - (touchPointOffset / 0.75), yPosition: touchPointYPos, treatmentArea: .upperForehead)
            let touchPointForehead5 = TouchPoint(touchPointSize: touchPointSize, xPosition: foreheadTopViewWidth - touchPointSize, yPosition: touchPointYPos, treatmentArea: .upperForehead)

            foreheadTopView.addSubview(touchPointForehead1)
            foreheadTopView.addSubview(touchPointForehead2)
            foreheadTopView.addSubview(touchPointForehead3)
            foreheadTopView.addSubview(touchPointForehead4)
            foreheadTopView.addSubview(touchPointForehead5)

            let foreheadGesture1 = UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:)))
            let foreheadGesture2 = UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:)))
            let foreheadGesture3 = UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:)))
            let foreheadGesture4 = UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:)))
            let foreheadGesture5 = UIPanGestureRecognizer(target: self, action: #selector(didPan(gesture:)))
            foreheadGesture1.delegate = self
            foreheadGesture2.delegate = self
            foreheadGesture3.delegate = self
            foreheadGesture4.delegate = self
            foreheadGesture5.delegate = self

            touchPointForehead1.addGestureRecognizer(foreheadGesture1)
            touchPointForehead2.addGestureRecognizer(foreheadGesture2)
            touchPointForehead3.addGestureRecognizer(foreheadGesture3)
            touchPointForehead4.addGestureRecognizer(foreheadGesture4)
            touchPointForehead5.addGestureRecognizer(foreheadGesture5)

            foreheadTopView.layer.addSublayer(touchPointForehead1.lineTo(touchpoint: touchPointForehead2))
            foreheadTopView.layer.addSublayer(touchPointForehead2.lineTo(touchpoint: touchPointForehead3))
            foreheadTopView.layer.addSublayer(touchPointForehead3.lineTo(touchpoint: touchPointForehead4))
            foreheadTopView.layer.addSublayer(touchPointForehead4.lineTo(touchpoint: touchPointForehead5))

@objc func didPan(gesture: UIPanGestureRecognizer) {

        guard let touchpoint = gesture.view as? TouchPoint else {
            return
        }
        if (gesture.state == .began) {
            touchpoint.center = gesture.location(in: foreheadTopView)
        }

        let newCenter: CGPoint = gesture.location(in: foreheadTopView)
        let dX = newCenter.x - touchpoint.center.x
        let dY = newCenter.y - touchpoint.center.y
        touchpoint.center = CGPoint(x: touchpoint.center.x + dX, y: touchpoint.center.y + dY)

        if let outGoingTouchPoint = touchpoint.outGoingTouchPoint, let line = touchpoint.outGoingLine, let path = touchpoint.outGoingLine?.path {
            let newPath = UIBezierPath(cgPath: path)
            newPath.removeAllPoints()
            newPath.move(to: touchpoint.center)
            newPath.addLine(to: outGoingTouchPoint.center)
            line.path = newPath.cgPath
        }

        if let inComingTouchPoint = touchpoint.inComingTouchPoint, let line = touchpoint.inComingLine, let path = touchpoint.inComingLine?.path {
            let newPath = UIBezierPath(cgPath: path)
            newPath.removeAllPoints()
            newPath.move(to: inComingTouchPoint.center)
            newPath.addLine(to: touchpoint.center)
            line.path = newPath.cgPath
        }
    }

就我需要它在平移/拖动方面做的事情而言,上面的代码工作正常.我还扩展了我的 ViewController 以符合 UIGestureRecognizerDelegate

The above code works fine in terms of what I need it to do in terms of panning/dragging. I have also extended my ViewController to conform to UIGestureRecognizerDelegate

extension ViewController: UIGestureRecognizerDelegate {
    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {

    }
}

但我不确定如何将可拖动视图限制在其父视图的边界内.有人可以帮忙吗?

But I am unsure where to go from here in how to restrict the draggable views within the boundaries of their parent view. Can anybody help?

推荐答案

不,delegate 不适合这件事.我相信你想保持手势,只是将你的观点的位置限制在他们的父母内部.为此,您需要将其限制在平移手势更改事件上.

No, delegate is not appropriate for this thing. I believe you want to keep gestures and just restrict the position of your views to be inside their parent. To do so you need to restrict that on pan gesture change event.

最简单的方法是检查两个相反的点是否在父矩形内:

The easiest is to check if two opposite points are within parent rect:

let topLeft = CGPoint(x: touchpoint.frame.minX + dX, y: touchpoint.frame.minY + dY)
let bottomRight = CGPoint(x: touchpoint.frame.maxX + dX, y: touchpoint.frame.maxY + dY)
if let containerView = touchpoint.superview, containerView.bounds.contains(topLeft), containerView.bounds.contains(bottomRight) {
    touchpoint.center = CGPoint(x: touchpoint.center.x + dX, y: touchpoint.center.y + dY)
}

所以基本上我们正在计算新的左上角和右下角点.如果这些在它们的超视图边界内,那么视图可能会移动到那个点.

So basically we are computing the new top-left and bottom-right points. And if these are inside their superview bounds then view may be moved to that point.

这很好,但问题是一旦发生这种情况,您的视图可能不会正好位于边界处.例如,如果用户非常快速地将它向左拖动,那么在某些时候 topLeft 可能是 (15, 0) 并且在下一个事件 (-10, 0) 这意味着它将忽略第二个并保持在 (15, 0).

This is pretty good but the thing is that once this occurs your view may not be exactly at the border. If user for instance drags it toward left really quickly then at some point topLeft may be (15, 0) and in the next event (-10, 0) which means it will ignore the second one and keep at (15, 0).

所以你需要实现限制移动并限制它...

So you need to implement the limi to movement and restrict it to it...

var targetFrame = CGRect(x: touchpoint.frame.origin.x + dX, y: touchpoint.frame.origin.y + dY, width: touchpoint.frame.width, height: touchpoint.frame.height)

if let containerView = touchpoint.superview {
    targetFrame.origin.x = max(0.0, targetFrame.origin.x)
    targetFrame.origin.y = max(0.0, targetFrame.origin.y)
    targetFrame.size.width = min(targetFrame.size.width, containerView.bounds.width-(targetFrame.origin.x+targetFrame.width))
    targetFrame.size.height = min(targetFrame.size.height, containerView.bounds.height-(targetFrame.origin.y+targetFrame.height))
}

touchpoint.frame = targetFrame

所以这应该让你的视图坚持在超级视图中......

So this should make your view stick inside the superview...

这篇关于设置平移手势的边界的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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