如何将可拖动的 UIView 限制在特定范围内

Posted

技术标签:

【中文标题】如何将可拖动的 UIView 限制在特定范围内【英文标题】:How to restrict a draggable UIView to specific bounds 【发布时间】:2016-06-02 14:29:55 【问题描述】:

我有一个 UIImageView,我通过一个名为 DraggableImageView2 的自定义类将其设为 Draggable。然后该类在带有图像的UIViewController 中实例化,并且可以在UIView 中拖动。问题是我想限制UIImageView 在拖动时不要越过特定边界。这是我当前的代码:

import UIKit

class DraggableImageView2: UIImageView, UIGestureRecognizerDelegate 

    var dragStartPositionRelativeToCenter : CGPoint?

    var dragGesture: UIGestureRecognizer!
    var zoomGesture: UIGestureRecognizer!

    var lastKnownCenterX: CGFloat = 0.0
    var lastKnownCenterY: CGFloat = 0.0

    required init?(coder aDecoder: NSCoder) 

        super.init(coder: aDecoder)

        self.initializeGestures()

    


    override init(image: UIImage?) 

        super.init(image: image)

        self.initializeGestures()

    

    override init(frame: CGRect) 

        super.init(frame: frame)

        self.initializeGestures()

    

    func initializeGestures() 

        self.userInteractionEnabled = true
        self.multipleTouchEnabled = true

        dragGesture = UIPanGestureRecognizer(target: self, action: #selector(DraggableImageView.handlePan(_:)))

        self.addGestureRecognizer(dragGesture)

    

    func handlePan(recognizer: UIPanGestureRecognizer!) 

        Scripts.log("PAN >>> MOVE ACTIVATED")

        if recognizer.state == UIGestureRecognizerState.Began 

            let locationInView = recognizer.locationInView(superview)
            dragStartPositionRelativeToCenter = CGPoint(x: locationInView.x - center.x, y: locationInView.y - center.y)

            return

        

        if recognizer.state == UIGestureRecognizerState.Ended 

            dragStartPositionRelativeToCenter = nil

            return
        

        let locationInView = recognizer.locationInView(superview)

        Scripts.log("PAN LOCATION >>> X = \(self.frame.origin.x) | Y = \(self.frame.origin.y) | LX = \(locationInView.x) | LY = \(locationInView.y) | DX = \(self.dragStartPositionRelativeToCenter!.x) | DY = \(self.dragStartPositionRelativeToCenter!.y)")

        let xDragMin: CGFloat = 1.0
        let yDragMin: CGFloat = 1.0
        let xDragMax: CGFloat = kDEVICE_WIDTH - self.frame.size.width - 1.0
        let yDragMax: CGFloat = kDEVICE_WIDTH - self.frame.size.height - 1.0

        var newCenterX: CGFloat = locationInView.x - self.dragStartPositionRelativeToCenter!.x
        var newCenterY: CGFloat = locationInView.y - self.dragStartPositionRelativeToCenter!.y

        if self.frame.origin.x < xDragMin 

            Scripts.log("PAN LOCATION >>> X MIN PAST BOUNDS")

            newCenterX =  lastKnownCenterX

        

        if self.frame.origin.y < yDragMin 

            Scripts.log("PAN LOCATION >>> Y MIN PAST BOUNDS")

            newCenterY = lastKnownCenterY

        

        if self.frame.origin.x > xDragMax 

            Scripts.log("PAN LOCATION >>> X MAX PAST BOUNDS")

        

        if self.frame.origin.y > yDragMax 

            Scripts.log("PAN LOCATION >>> Y MAX PAST BOUNDS")

        


        UIView.animateWithDuration(0.1) 
            self.center = CGPoint(x: newCenterX,
                                  y: newCenterY)
        

        lastKnownCenterX = newCenterX
        lastKnownCenterY = newCenterY

    


    func handlePinch(recognizer: UIPinchGestureRecognizer!) 

        Scripts.log("PINCH >>> ZOOM ACTIVATED")

        //self.bringSubviewToFront(recognizer.view!)

        recognizer.view?.transform = CGAffineTransformScale((recognizer.view?.transform)!, recognizer.scale, recognizer.scale)
        recognizer.scale = 1

    

    func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool 

        Scripts.log("shouldRecognizeSimultaneouslyWithGestureRecognizer WAS CALLED")

        return true

    


我才开始研究xDragMinyDragMin 部分。当它小于 xDragMin 时,一切都可以阻止它,但是由于某种奇怪的原因,当你试图拖出它时,它会冻结在那个 X 点。 yDragMin 也一样,如果达到最小值,它会按原样停止,但不能拖出 Y 点。

【问题讨论】:

您可以尝试记录lastKnownCenterXlastKnownCenterY 吗?如果这些值以某种方式设置为小于 xDragMinxDragY 的值,那么您的视图框架肯定会卡在该位置。 @KyleParent 刚刚尝试记录它。不,似乎最后一个已知点的设置小于 Drag Min Points。这是一个冻结的示例输出: LAST KNOWN LOCATION :PRE: >>> X = 56.75 | Y = 301.75 最后一个已知位置 :POST: >>> X = 56.75 | Y = 301.75 lastKnownCenterX 为56.75 时,视图的frame.origin.x 是多少? 啊,所以我的怀疑是正确的 - 不知何故,您的视图被允许通过 xDragMin 阈值。一旦发生这种情况,您将始终点击第一个条件并设置newCenterX = lastKnownCenterX,其中lastKnownCenterX 已经离左边太远了。我建议重新审视用于计算 newCenterXnewCenterX 的逻辑。不应该是var newCenterX: CGFloat = locationInView.x + self.dragStartPositionRelativeToCenter!.x吗? @KyleParent 这不起作用,但我认为您的线索和建议帮助我确定了我认为问题所在,尤其是在添加更多日志之后。我认为的问题是这种逻辑“如果 self.frame.origin.x 【参考方案1】:

所以经过更多的挖掘和玩耍之后,我似乎已经能够解决我自己的问题,至少对于 X Bounds Min 而言。这是我所做的:

(1) 添加了这些新变量

var isWithinXBounds: Bool = true
var lastKnownLX: CGFloat = 0.0

(2) 以“if self.frame.origin.x

  if self.frame.origin.x < xDragMin 

        Scripts.log("PAN LOCATION >>> X MIN PAST BOUNDS")

        self.isWithinXBounds = false

        //If TOUCH still moving in negative direction
        if locationInView.x - lastKnownLX  < 0 

            newCenterX =  lastKnownCenterX

        
        else 

            self.isWithinXBounds = true

        

    

(3) 下面是根据 BOOL 值解锁 UIImageView 的条件:

if isWithinXBounds 

        lastKnownCenterX = newCenterX
        lastKnownLX = locationInView.x

    

【讨论】:

以上是关于如何将可拖动的 UIView 限制在特定范围内的主要内容,如果未能解决你的问题,请参考以下文章

如何将神经网络的输出限制在特定范围内?

ios -- 如何限制 CAEmitter 粒子轨迹的范围?

使用javascript限制在特定div窗口上的html内拖动对象

jQueryUI:如何将可放置区域限制为多个 div?

限制仅向一个方向拖动

如何将可拖动对象放置在目标对象的中心