当我结合使用UIPanGestureRecognizer和自动布局时,我的UIViews混乱了

时间:2016-11-30 10:16:46

标签: ios swift uiview autolayout uipangesturerecognizer

我想要一个球跟踪我的手指,因为我沿着圆形轨迹拖动它,以便在iPhone或iPad上为每个允许的设备方向。当旋转设备时,视图似乎正确居中,但是当我拖动它时,球不会停留在圆周上并且似乎在任何地方。

修改

Martin R's answer现在根据需要显示此内容。我唯一的额外代码更改是删除不必要的声明var shapeLayer = CAShapeLayer()

enter image description here

this example中的数学运算非常有意义,直到我尝试将球和轨迹约束到视图的中心并在运行时将球的中心坐标添加为偏移。我跟着these recommendations on how to constrain a view

有三件事我不明白。

首先,从两个变量trackRadius和角度theta计算圆周长,并使用sin的{​​{1}}和cos来查找theta并且x坐标不会将球放在正确的位置。

其次,使用y查找视图中心与所触摸点之间的角度atan,并使用thetatrackRadius查找thetax坐标不会将球放置或移动到圆周上的新位置。

第三,每当我拖动球时,调试区域中的消息都会显示y,尽管在拖动之前没有报告任何约束问题。

这里可能存在多个问题。我的大脑开始受到伤害,如果有人能指出我做错了什么,我将不胜感激。

这是我的代码。

Xcode is Unable to simultaneously satisfy constraints

1 个答案:

答案 0 :(得分:4)

主要错误是

theta = CGFloat(atan2(Double(finger.x), Double(finger.y)))   // get angle from finger tip to centre

不考虑(或跟踪) center ,并且atan2()的参数是错误的方式(y先来)。它应该是:

theta = atan2(finger.y - track.center.y, finger.x - track.center.x)

另一个问题是你添加了越来越多的约束 在func constrainBall()中,没有删除以前的。 您应该保留对约束的引用并改为修改它们。

最后请注意,球的宽度/高度约束应为2*ballRadius,而不是trackRadius

全部放在一起(并删除一些不必要的类型 转换),它看起来像这样:

var ballXconstraint: NSLayoutConstraint!
var ballYconstraint: NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()
    createTrack()
    createBall()

    let touch = UIPanGestureRecognizer(target: self, action:#selector(dragBall(recognizer:)))
    view.addGestureRecognizer(touch)
}

private func createTrack() {
    track.translatesAutoresizingMaskIntoConstraints = false
    track.shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 2 * trackRadius, height: 2 * trackRadius)).cgPath
    track.shapeLayer.fillColor      = UIColor.clear.cgColor
    track.shapeLayer.strokeColor    = UIColor.red.cgColor
    view.addSubview(track)

    track.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    track.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    track.widthAnchor.constraint(equalToConstant: 2 * trackRadius).isActive = true
    track.heightAnchor.constraint(equalToConstant: 2 * trackRadius).isActive = true
}

private func createBall() {

    // Create ball:
    ball.translatesAutoresizingMaskIntoConstraints = false
    ball.shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: 0, y: 0, width: 2 * ballRadius, height: 2 * ballRadius)).cgPath
    ball.shapeLayer.fillColor    = UIColor.cyan.cgColor
    ball.shapeLayer.strokeColor  = UIColor.black.cgColor
    view.addSubview(ball)

    // Width/Height contraints:
    ball.widthAnchor.constraint(equalToConstant: 2 * ballRadius).isActive = true
    ball.heightAnchor.constraint(equalToConstant: 2 * ballRadius).isActive = true

    // X/Y constraints:
    let offset = pointOnCircumference(0.0)
    ballXconstraint = ball.centerXAnchor.constraint(equalTo: track.centerXAnchor, constant: offset.x)
    ballYconstraint = ball.centerYAnchor.constraint(equalTo: track.centerYAnchor, constant: offset.y)
    ballXconstraint.isActive = true
    ballYconstraint.isActive = true
}

func dragBall(recognizer: UIPanGestureRecognizer) {

    let finger = recognizer.location(in: self.view)

    // Angle from track center to touch location:
    theta = atan2(finger.y - track.center.y, finger.x - track.center.x)

    // Update X/Y contraints of the ball:
    let offset = pointOnCircumference(theta)
    ballXconstraint.constant = offset.x
    ballYconstraint.constant = offset.y
}


private func pointOnCircumference(_ theta: CGFloat) -> CGPoint {
    let x = cos(theta) * trackRadius
    let y = sin(theta) * trackRadius
    return CGPoint(x: x, y: y)
}