设置平移手势的边界
Setting boundaries on pan gestures
我创建了 5 个 UIView,每个都带有 UIPanGestureRecognizers,然后我将其添加到另一个 UIView。所有这些都有效,我可以四处拖动,但我不希望它们被拖到它们的父 UIView 的边界之外。我读过我可以使用 gestureRecognizerShouldBegin 函数实现 UIGestureRecognizerDelegate 来实现这一点,但是我找不到足够的文档来准确说明如何实现我所需要的。像往常一样,Apple 对此的文档非常模糊。我的代码是:
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
}
}
根据 panning/dragging 的要求,上面的代码工作正常。我还扩展了我的 ViewController 以符合 UIGestureRecognizerDelegate
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
}
}
但我不确定如何将可拖动视图限制在其父视图的边界内。有人可以帮忙吗?
你要做一些计算,希望这段代码能帮到你。
@objc func handlepan(_ gesture:UIPanGestureRecognizer)
{
let translation = gesture.translation(in: self.imagelayer)
if let view1 = gesture.view {
if (view1.frame.origin.x + translation.x >= 0) && (view1.frame.origin.y + translation.y >= 0) && (view1.frame.origin.x + translation.x <= view1.frame.width ) && (view1.frame.origin.y + translation.y <= view1.frame.height)
{
view1.center = CGPoint(x:view1.center.x + translation.x,
y:view1.center.y + translation.y)
}
}
gesture.setTranslation(CGPoint.zero, in: self.imagelayer)
}
不,委托不适合做这件事。我相信你想保持手势并且只是将你的视图的位置限制在他们的 parent 内。为此,您需要限制平移手势更改事件。
最简单的是检查两个相对的点是否在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)
}
所以基本上我们正在计算新的 top-left 和 bottom-right 点。如果它们在它们的超级视图范围内,则视图可能会移动到该点。
这很好,但问题是,一旦发生这种情况,您的视图可能不在边界处。例如,如果用户非常快地将它向左拖动,那么在某个时候 topLeft
可能是 (15, 0)
并且在下一个事件中 (-10, 0)
这意味着它将忽略第二个并保持在 (15, 0)
.
所以你需要实现移动限制并限制它...
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
所以这应该使您的视图停留在超级视图中...
我创建了 5 个 UIView,每个都带有 UIPanGestureRecognizers,然后我将其添加到另一个 UIView。所有这些都有效,我可以四处拖动,但我不希望它们被拖到它们的父 UIView 的边界之外。我读过我可以使用 gestureRecognizerShouldBegin 函数实现 UIGestureRecognizerDelegate 来实现这一点,但是我找不到足够的文档来准确说明如何实现我所需要的。像往常一样,Apple 对此的文档非常模糊。我的代码是:
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
}
}
根据 panning/dragging 的要求,上面的代码工作正常。我还扩展了我的 ViewController 以符合 UIGestureRecognizerDelegate
extension ViewController: UIGestureRecognizerDelegate {
func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
}
}
但我不确定如何将可拖动视图限制在其父视图的边界内。有人可以帮忙吗?
你要做一些计算,希望这段代码能帮到你。
@objc func handlepan(_ gesture:UIPanGestureRecognizer)
{
let translation = gesture.translation(in: self.imagelayer)
if let view1 = gesture.view {
if (view1.frame.origin.x + translation.x >= 0) && (view1.frame.origin.y + translation.y >= 0) && (view1.frame.origin.x + translation.x <= view1.frame.width ) && (view1.frame.origin.y + translation.y <= view1.frame.height)
{
view1.center = CGPoint(x:view1.center.x + translation.x,
y:view1.center.y + translation.y)
}
}
gesture.setTranslation(CGPoint.zero, in: self.imagelayer)
}
不,委托不适合做这件事。我相信你想保持手势并且只是将你的视图的位置限制在他们的 parent 内。为此,您需要限制平移手势更改事件。
最简单的是检查两个相对的点是否在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)
}
所以基本上我们正在计算新的 top-left 和 bottom-right 点。如果它们在它们的超级视图范围内,则视图可能会移动到该点。
这很好,但问题是,一旦发生这种情况,您的视图可能不在边界处。例如,如果用户非常快地将它向左拖动,那么在某个时候 topLeft
可能是 (15, 0)
并且在下一个事件中 (-10, 0)
这意味着它将忽略第二个并保持在 (15, 0)
.
所以你需要实现移动限制并限制它...
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
所以这应该使您的视图停留在超级视图中...