Setting boundaries on pan gestures

ε祈祈猫儿з 提交于 2019-12-24 10:39:34

问题


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
        }
    }

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?


回答1:


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.

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




回答2:


You have to do some calculations, I hope this code helps you.

@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)
}


来源:https://stackoverflow.com/questions/49008643/setting-boundaries-on-pan-gestures

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!