2016-11-30 14 views
5

Tôi muốn một bóng để theo dõi ngón tay của tôi khi tôi kéo nó dọc theo một quỹ đạo hình tròn cho mỗi hướng thiết bị cho phép trên iPhone hoặc iPad. Lần xuất hiện để được tập trung một cách chính xác khi một thiết bị được quay nhưng quả bóng sẽ không ở lại trên chu vi và dường như đi đâu khi tôi kéo nó.UIViews tôi muck-up khi tôi kết hợp UIPanGestureRecognizer và autolayout


EDIT

Martin R's answer bây giờ hiển thị này theo yêu cầu. chỉ thêm thay đổi mã của tôi là để loại bỏ một tuyên bố không cần thiết var shapeLayer = CAShapeLayer()

enter image description here


Các toán trong this example có ý nghĩa hoàn hảo cho đến khi tôi cố gắng kìm hãm cả bóng và quỹ đạo đến trung tâm của quan điểm và thêm trung tâm của quả bóng phối như bù đắp tại thời gian chạy. Tôi đã theo dõi these recommendations on how to constrain a view.

Có ba điều tôi không hiểu.

Thứ nhất, tính chu vi của vòng tròn từ hai biến trackRadius và góc theta và sử dụng sincos của theta để tìm xy tọa độ sẽ không đặt bóng vào đúng vị trí.

Thứ hai, sử dụng atan để tìm góc theta giữa trung tâm xem và điểm chạm, và sử dụng trackRadius với theta để tìm xy tọa độ sẽ không đặt hoặc di chuyển quả bóng đến một nơi mới dọc theo chu vi.

Và thứ ba, bất cứ khi nào tôi kéo bóng, một thông báo trong khu vực gỡ lỗi nói rằng Xcode is Unable to simultaneously satisfy constraints, mặc dù không có vấn đề ràng buộc nào được báo cáo trước khi kéo nó.

Có thể có nhiều hơn một vấn đề ở đây. Não của tôi đang bắt đầu đau và tôi sẽ biết ơn nếu ai đó có thể chỉ ra những gì tôi đã làm sai.

Đây là mã của tôi.

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

Trả lời

4

Lỗi chính là

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

không mất quan điểm (hay theo dõi) trung tâm vào tài khoản, và rằng các đối số atan2() là một cách sai lầm xung quanh (y đến trước) . Nó nên là:

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

Một vấn đề khác là bạn có thêm ngày càng nhiều contraints trong func constrainBall(), mà không loại bỏ những cái cũ. Bạn nên giữ tham chiếu đến các ràng buộc và sửa đổi chúng thay thế.

Cuối cùng lưu ý rằng giới hạn chiều rộng/chiều cao cho quả bóng phải là 2*ballRadius, không phải trackRadius.

Đưa nó tất cả cùng nhau (và loại bỏ một số loại không cần thiết chuyển đổi), nó sẽ trông như thế này:

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

sau một đêm ngon giấc, những gì một niềm vui nó được thức dậy vào đó! Bạn đã đóng đinh mọi thứ tôi đã thử và làm cho mã Swift tự giải thích hơn trong quá trình này. Nó phải là câu trả lời được chấp nhận. – Greg

Các vấn đề liên quan