2016-11-30 45 views
5

'u bir araya getirdiğimde UIViews muck-up, iPhone veya iPad'de izin verilen her cihaz yönlendirmesi için dairesel bir yörünge boyunca sürüklerken parmağımı izlemek için bir top istiyorum. Bir cihaz döndürüldüğünde görünümler doğru ortalanmış gibi görünür ancak top çevrede kalmaz ve sürüklediğimde herhangi bir yere gitmiş gibi görünür. gerektiği gibiUIPiGestureRecognizer ve autolayout


DÜZENLEME

Martin R's answer şimdi bu görüntüler. Benim tek ek kod değişikliği gereksiz bir deklarasyon var shapeLayer = CAShapeLayer()

enter image description here


Ben görünümün merkezine hem topu ve yörünge kısıtlayıcı ve topun merkezi olarak koordinatları ekleyerek çalıştı kadar this example yılında matematik mükemmel mantıklı geldi kaldırmak oldu çalışma zamanında ofsetler. these recommendations on how to constrain a view'u takip ettim.

Anlamadığım üç şey var.

Birincisi, çemberin iki değişkenlerden çevresini trackRadius ve açısını theta hesaplanması ve doğru pozisyonda topu yerleştirmek olmaz x ve y koordinatlarını bulmak için sin ve theta ait cos kullanarak. görünüm merkezi ve dokundu nokta arasındaki açıyı theta bulmak için atan kullanarak ve yerleştirmek veya çevresi boyunca yeni bir yere topu hareket etmeyecektir x ve y koordinatlarını bulmak için theta ile trackRadius kullanılarak

İkincisi,.

Ve üçüncü olarak, topu her sürüklediğimde, hata ayıklama alanında bir ileti, sürükleme öncesinde hiçbir kısıtlama sorunu bildirilmemesine rağmen, Xcode is Unable to simultaneously satisfy constraints diyor.

Burada birden fazla sorun olabilir. Beynim incinmeye başlıyor ve eğer yanlış yaptığım şeyi işaret ederse minnettar olacağım.

İşte kodum.

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

cevap

4

ana hata

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

görüşlerini (veya parçayı) dikkate merkezi almaz olması ve atan2() için argümanlar etrafında yanlış bir yol olduğunu (y önce gelir) . Olmalıdır:

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

başka sorun önceki olanları çıkarmadan, func constrainBall() yılında daha contraints eklemek olmasıdır. Kısıtlamalara referanslar tutmalı ve bunları değiştirmelisiniz.

Son olarak, top için genişlik/yükseklik sınırının , değil trackRadius olması gerektiğini unutmayın.

Hepsini bir araya getirirsek (ve bazı gereksiz tip dönüşüm kaldırma), bu şekilde görünecektir:

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

iyi bir gece uykusunun ardından, ne bu kadar uyanma bir sevinç! Denediğim her şeyi kırdın ve Swift kodunu bu süreçte daha açıklayıcı yaptı. Kabul edilen cevap olmalı. – Greg