设置平移手势的边界 [英] Setting boundaries on pan gestures
问题描述
我创建了 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屋!