2017-08-06 1 views
1

Ich benutze CAShapeLayer, um eine Linie auf dem Bildschirm zu zeichnen. In der Methode touchesEnded Ich möchte überprüfen, "Passiert die Linie durch den Punkt?". In meinem Code, wenn ich auf einen beliebigen Teil des Bildschirms drücken, die Methode enthält gibt immer wahr. Vielleicht habe ich ein Problem in line.frame = (view? .bounds)!. Wie kann ich es reparieren? Sorry für mein schlechtes Englisch.CAShapeLayer. Geht die Linie durch den Punkt?

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { 

    let touch = touches.first 
    let firstPosition = touch?.location(in: self) 

    if atPoint(firstPosition!) == lvl1 { 

     let firstPositionX = firstPosition?.x 
     let firstPositionY = frame.size.height - (firstPosition?.y)! 
     view?.layer.addSublayer(line) 
     line.lineWidth = 8 
     let color = #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1).cgColor 
     line.strokeColor = color 
     line.fillColor = nil 
     line.frame = (view?.bounds)! 
     path.move(to: CGPoint(x: firstPositionX!, y: firstPositionY)) 

    } 

} 

override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { 

    let touch = touches.first 
    let firstPosition = touch?.location(in: self) 

    if atPoint(firstPosition!) == lvl1 { 

     let firstPositionX = firstPosition?.x 
     let firstPositionY = frame.size.height - (firstPosition?.y)! 
     path.addLine(to: CGPoint(x: firstPositionX!, y: firstPositionY)) 
     line.path = path.cgPath 

    } 
} 


override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { 

    if line.contains(screenCenterPoint) { 
     print("ok") 
    } 

} 
+0

Ich würde vorschlagen, den Abstand zwischen einem [Liniensegment und einem Punkt] (http://en.wikipedia.org/wiki/Distance_from_a_point_to_a_line) zu berechnen und sehen, ob es unter einen Wert fällt. – Rob

Antwort

3

Problem

Verfahren func contains(_ p: CGPoint) -> Bool von CAShapeLayer kehrt true wenn die Grenzen der Schicht den Punkt enthält. (see documentation)

So können Sie nicht verwenden, um zu überprüfen, ob die Zeile einen Punkt enthält.

Es gibt jedoch eine andere Methode mit demselben Namen in der Klasse CGPath, die zurückgibt, ob der angegebene Punkt interior zum Pfad ist. Aber da Sie nur Strich Ihren Weg und Sie nicht füllen das Innere, wird diese Methode nicht das gewünschte Ergebnis auch nicht geben.

Lösung

Der Trick ist eine Umriss von Ihrem Weg zu schaffen mit:

let outline = path.cgPath.copy(strokingWithWidth: line.lineWidth, lineCap: .butt, lineJoin: .round, miterLimit: 0) 

Und dann zu prüfen, ob das Innere der Gliederung enthält Ihre screenCenterPoint

if outline.contains(screenCenterPoint) { 
    print("ok") 
} 

Stroke vs outline

nicht hinzufügen zu viel Aufwand

Leistungsaspekte

Da Sie die Eindämmung sind die Überprüfung nur dann, wenn berührt Ende, denke ich, dass eine Skizze des Weges zu schaffen.

Wenn Sie wollen, die Eindämmung in Echtzeit zu überprüfen, zum Beispiel in der touchesMoved Funktion, einen Umriss Berechnung gewissen Overhead erzeugen kann, da diese Methode viele Male pro Sekunde aufgerufen wird. Je länger der Pfad wird, desto länger dauert die Berechnung des Umrisses.

Also in Echtzeit ist es besser, nur die Umrisse des letzten gezeichneten Segments zu generieren und dann zu überprüfen, ob diese Umrisse Ihren Punkt enthalten.

Wenn Sie den Overhead stark reduzieren möchten, können Sie Ihre eigene Containment-Funktion schreiben.Containment eines Punktes in einer geraden Linie ist relativ einfach und kann auf der folgenden Formel reduziert werden:

eine Linie von start zu end mit width und einem Punkt Gegeben p

berechnen:

  • dx = start.x - end.x
  • dy = start.y - end.y
  • a = dy * p.x - dx * p.y + end.x * start.y - end.y * start.x
  • b = hypot(dy, dx)

Die Zeile enthält Punkt p wenn:

abs(a/b) < width/2 und p in dem Begrenzungskasten der Linie ist.

+0

Erstaunliche Antwort! –

+0

Vielen Dank. Es hat mein Problem gelöst. –

Verwandte Themen