2016-11-30 18 views
5

我想要一个球跟踪我的手指,因为我沿着圆形轨迹将它拖动到iPhone或iPad上的每个允许的设备方向。当设备旋转时,视图看起来正确居中,但球不会停留在圆周上,并且当我拖动它时似乎会到达任何位置。当我将UIPanGestureRecognizer和自动布局结合起来时,我的UIViews弄脏了


EDIT

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

enter image description here


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

有三件事我不明白。

首先,计算从两个变量的圆的周长trackRadius和角度theta和使用sinthetacos找到xy坐标将不把球在合适的位置。

二,用atan找到观察中心,碰到点之间的角度theta,并使用trackRadiustheta找到xy坐标将不放置或移动球沿圆周一个新的地方。第三,无论何时拖动球,调试区域中的消息都表示Xcode is Unable to simultaneously satisfy constraints,尽管在拖动它之前没有报告约束问题。

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

这是我的代码。

import UIKit 

class ViewController: UIViewController { 

override var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .all } 
var shapeLayer      = CAShapeLayer() 
let track       = ShapeView() 
var ball       = ShapeView() 
var theta       = CGFloat() 

private let trackRadius: CGFloat = 125 
private let ballRadius: CGFloat  = 10 

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

private func createTrack() { 
    track.translatesAutoresizingMaskIntoConstraints = false 
    track.shapeLayer.path = UIBezierPath(ovalIn: CGRect(x: -trackRadius, y: -trackRadius, 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 
} 

private func createBall() { 

    let offset = placeBallOnCircumference() 

    drawBall() 
    constrainBall(offset: offset) 

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

private func placeBallOnCircumference() -> CGPoint { 
    let theta: Double = 0           // at 0 radians 
    let x = CGFloat(cos(theta)) * trackRadius      // find x and y coords on 
    let y = CGFloat(sin(theta)) * trackRadius      // circle circumference 
    return CGPoint(x: x, y: y) 
} 

func dragBall(recognizer: UIPanGestureRecognizer) { 

    var offset = CGPoint() 

    let finger : CGPoint = recognizer.location(in: self.view) 
    theta = CGFloat(atan2(Double(finger.x), Double(finger.y))) // get angle from finger tip to centre 
    offset.x = CGFloat(cos(theta)) * trackRadius     // use angle and radius to get x and 
    offset.y = CGFloat(sin(theta)) * trackRadius     // y coords on circle circumference 

    drawBall() 
    constrainBall(offset: offset) 
} 

private func drawBall() { 
    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) 
} 

private func constrainBall(offset: CGPoint) { 
    ball.translatesAutoresizingMaskIntoConstraints = false 
    NSLayoutConstraint.activate([ 
     ball.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: offset.x), 
     ball.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: offset.y), 
     ball.widthAnchor.constraint(equalToConstant: trackRadius), 
     ball.heightAnchor.constraint(equalToConstant: trackRadius) 
     ]) 
    } 
} 

回答

4

主要错误是,

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

不走意见(或轨道)中心进去了,该参数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) 
} 
+1

一个良好的夜间睡眠后,什么是快乐是意识到了这个!你钉住了我试过的所有东西,并使Swift代码在这个过程中更加自我解释。它必须是被接受的答案。 – Greg

相关问题