Ob Sie drawLayer(_:inContext:)
oder drawRect(_:)
(oder beide) für benutzerdefinierten Zeichnungscode verwenden, hängt davon ab, ob Sie während der Animation Zugriff auf den aktuellen Wert einer Ebeneneigenschaft benötigen.
Ich hatte heute Probleme mit verschiedenen Rendering-Problemen im Zusammenhang mit diesen beiden Funktionen, als ich meine eigene Label-Klasse implementierte . Nachdem ich die Dokumentation durchgesehen, einige Versuche und Irrtümer durchgeführt, UIKit dekompiliert und das Beispiel für benutzerdefinierte animierbare Eigenschaften von Apple überprüft hatte, bekam ich ein gutes Gefühl dafür, wie es funktioniert.
drawRect(_:)
Wenn Sie während der Animation nicht auf den aktuellen Wert einer Ebenen- / Ansichtseigenschaft zugreifen müssen, können Sie einfach drawRect(_:)
Ihre benutzerdefinierte Zeichnung ausführen. Alles wird gut funktionieren.
override func drawRect(rect: CGRect) {
}
drawLayer(_:inContext:)
Angenommen, Sie möchten backgroundColor
in Ihrem benutzerdefinierten Zeichnungscode Folgendes verwenden:
override func drawRect(rect: CGRect) {
let colorForCustomDrawing = self.layer.backgroundColor
}
Wenn Sie Ihren Code testen, werden Sie feststellen, dass backgroundColor
während des Flugs einer Animation nicht der richtige (dh der aktuelle) Wert zurückgegeben wird. Stattdessen wird der endgültige Wert zurückgegeben (dh der Wert für den Abschluss der Animation).
Um den aktuellen Wert während der Animation zu erhalten, müssen Sie auf backgroundColor
den layer
Parameter zugreifen, an den übergeben wurde drawLayer(_:inContext:)
. Und Sie müssen auch auf den context
Parameter zeichnen .
Es ist sehr wichtig zu wissen, dass die Ansicht self.layer
und der übergebene layer
Parameter drawLayer(_:inContext:)
nicht immer dieselbe Ebene sind! Letzteres könnte eine Kopie des ersteren sein, wobei teilweise Animationen bereits auf seine Eigenschaften angewendet wurden. Auf diese Weise können Sie auf korrekte Eigenschaftswerte von Animationen während des Flugs zugreifen.
Jetzt funktioniert die Zeichnung wie erwartet:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
let colorForCustomDrawing = layer.backgroundColor
}
Es gibt jedoch zwei neue Probleme: setNeedsDisplay()
und einige Eigenschaften wie backgroundColor
und opaque
funktionieren für Ihre Ansicht nicht mehr. UIView
leitet Anrufe und Änderungen nicht mehr an die eigene Ebene weiter.
setNeedsDisplay()
tut nur etwas, wenn Ihre Ansicht implementiert wird drawRect(_:)
. Es spielt keine Rolle, ob die Funktion tatsächlich etwas tut, aber UIKit verwendet sie, um zu bestimmen, ob Sie benutzerdefinierte Zeichnungen erstellen oder nicht.
Die Eigenschaften funktionieren wahrscheinlich nicht mehr, da UIView
die eigene Implementierung von drawLayer(_:inContext:)
nicht mehr aufgerufen wird.
Die Lösung ist also ganz einfach. Rufen Sie einfach die Implementierung der Superklasse auf drawLayer(_:inContext:)
und implementieren Sie eine leere drawRect(_:)
:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
super.drawLayer(layer, inContext: context)
let colorForCustomDrawing = layer.backgroundColor
}
override func drawRect(rect: CGRect) {
}
Zusammenfassung
Verwenden drawRect(_:)
Sie diese Option , solange Sie nicht das Problem haben, dass Eigenschaften während einer Animation falsche Werte zurückgeben:
override func drawRect(rect: CGRect) {
}
Verwenden Sie drawLayer(_:inContext:)
und, drawRect(_:)
wenn Sie auf den aktuellen Wert der Ansichts- / Ebeneneigenschaften zugreifen müssen, während diese animiert werden:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
super.drawLayer(layer, inContext: context)
let colorForCustomDrawing = layer.backgroundColor
}
override func drawRect(rect: CGRect) {
}
AccelerometerGraph
. Ich vermute, dass sie in diesem Codebeispiel implementiert wurden,drawLayer:inContext:
da der Ebenenhierarchie neben der Stammschicht der Ansicht einige Ebenen hinzugefügt wurden und der Delegat tatsächlich nicht dieUIView
Instanz ist.