setNeedsDisplay() ist eine Instanzmethode von UIView, die die View als neuzeichnungsbedürftig markiert. Im Gegensatz zum direkten Aufruf von draw(_:) setzt setNeedsDisplay() nur das dirty-Flag für die View und gibt sofort die Kontrolle zurück. Das System verarbeitet diese Anforderung im nächsten Renderzyklus, wodurch mehrere Aufrufe zu einem einzigen Zeichenvorgang zusammengefasst werden können. Laut Apple Documentation (2025) ist das verzögerte Neuzeichnen über setNeedsDisplay() die einzig korrekte Methode, um den Inhalt einer benutzerdefinierten UIView zu aktualisieren.
Wichtigste Erkenntnisse
setNeedsDisplay() ist eine UIView-Methode, die dem System signalisiert, dass der Inhalt der View veraltet ist und neu gerendert werden muss. Die Methode nimmt keine Parameter entgegen und gibt void zurück. Nach dem Aufruf wird die View als dirty markiert, und im nächsten Zeichenzyklus ruft das System draw(_:) für diese View auf.
Das Hauptmerkmal ist die Asynchronität. Mehrere aufeinanderfolgende Aufrufe von setNeedsDisplay() für dieselbe View führen nicht zu mehrfachen draw(_:)-Aufrufen — das System fasst sie zu einem einzigen Vorgang zusammen. Dies ist für die Leistung von entscheidender Bedeutung: Wenn sich eine View-Eigenschaft mehrmals pro Frame ändert, wird draw(_:) nur einmal aufgerufen.
Der Mechanismus des verzögerten Neuzeichnens basiert auf dem UIKit-Renderzyklus. Wenn setNeedsDisplay() aufgerufen wird, setzt das System das interne Flag layer.needsDisplay auf true. Am Ende des aktuellen Run Loops durchläuft das System alle dirty-Layer und ruft für jeden draw(_:) auf.
Der UIKit-Run-Loop enthält eine Update-Phase, in der dirty-Views verarbeitet werden. In dieser Phase wird bei Bedarf layoutSubviews() aufgerufen, dann draw(_:) für alle markierten Views. Nach Abschluss der Zeichnung wird das Ergebnis über Core Animation zur Compositing an die GPU übergeben.
class ProgressView: UIView {
var progress: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let ctx = UIGraphicsGetCurrentContext() else { return }
// Hintergrund
ctx.setFillColor(UIColor.lightGray.cgColor)
ctx.fill(rect)
// Fortschrittsbalken
let fillRect = CGRect(x: rect.minX, y: rect.minY,
width: rect.width * progress, height: rect.height)
ctx.setFillColor(UIColor.systemGreen.cgColor)
ctx.fill(fillRect)
}
}
In diesem Beispiel aktualisiert progressView progress, und jeder Setter ruft automatisch setNeedsDisplay() auf. Das System wartet das Ende des Run Loops ab und ruft draw(_:) mit dem tatsächlichen progress-Wert auf. Wenn sich progress dreimal pro Frame ändert, wird draw(_:) nur einmal ausgeführt — mit dem letzten Wert.
Die Methode setNeedsDisplay(_:) nimmt einen rect-Parameter vom Typ CGRect entgegen, der den Neuzeichnungsbereich begrenzt. Dadurch kann nur der geänderte Teil der View neu gezeichnet werden, anstatt der gesamten View. Der Leistungsunterschied wird bei großen Views deutlich, bei denen Änderungen einen kleinen Bereich betreffen.
| Eigenschaft | setNeedsDisplay() | setNeedsDisplay(_:) |
|---|---|---|
| Parameter | Keiner | CGRect — Neuzeichnungsbereich |
| Neuzeichnungsbereich | Gesamte View-Bounds | Nur das angegebene Rechteck |
| Leistung | Niedriger bei großen Views | Höher bei teilweisen Änderungen |
| rect-Parameter in draw(_:) | Gesamte Bounds | Angegebenes Rechteck |
| Verwendung | Änderung des gesamten Inhalts | Punktuelle Änderungen (Cursor, Hervorhebung) |
Für komplexe Grafiken wird empfohlen, die gesamte Szene zuerst in einem Offscreen-Kontext zu zeichnen und dann setNeedsDisplay(_:) nur für den geänderten Bereich aufzurufen. Dies ist eine Standardtechnik in Zeichenanwendungen und Grafikeditoren unter iOS.
displayIfNeeded() ist eine synchrone Methode, die sofort das Neuzeichnen aller dirty-Views in der Hierarchie auslöst, ohne auf das Ende des Run Loops zu warten. Im Gegensatz zu setNeedsDisplay(), das nur ein Flag setzt, erzwingt displayIfNeeded() die sofortige Ausführung von draw(_:).
Apple empfiehlt, displayIfNeeded() nur in extremen Fällen zu verwenden, wenn das Zeichenergebnis sofort benötigt wird — zum Beispiel vor dem Erstellen eines Screenshots oder bei der synchronen Bildgenerierung für UIGraphicsImageRenderer. In normalen Szenarien ist das asynchrone Modell über setNeedsDisplay() vorzuziehen, da es den Hauptthread nicht blockiert.
// Asynchron — empfohlen
someView.setNeedsDisplay()
// Synchron — nur bei Bedarf
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) sofort aufgerufen
Ein Aufruf von displayIfNeeded() ohne vorheriges setNeedsDisplay() hat keine Wirkung — die Methode verarbeitet nur bereits als dirty markierte Views. Wenn keine View neu gezeichnet werden muss, wird displayIfNeeded() ohne Aufruf von draw(_:) abgeschlossen.
Häufige setNeedsDisplay()-Aufrufe können die Leistung beeinträchtigen, insbesondere bei Animationen. Betrachten wir die wichtigsten Optimierungsstrategien.
Anstatt setNeedsDisplay() bei jeder Änderung einer einzelnen Eigenschaft aufzurufen, sammeln Sie Änderungen und rufen Sie die Methode einmal nach Anwendung aller Aktualisierungen auf. Wenn Sie beispielsweise Farbe, Größe und Position eines Elements ändern — rufen Sie setNeedsDisplay() einmal nach dem Setzen aller Eigenschaften auf.
Wenn eine Eigenschaft über CALayer (backgroundColor, opacity, position, transform) animiert werden kann, verwenden Sie Core Animation anstelle von draw(_:). CALayer-Eigenschaften werden auf der GPU ohne Aufruf von draw(_:) aktualisiert und bieten 60 FPS ohne CPU-Belastung.
Um mehrere Änderungen in einem einzigen Renderdurchlauf zu gruppieren, verwenden Sie CATransaction. Dies ermöglicht die Zusammenfassung mehrerer setNeedsDisplay()-Aufrufe zu einem einzigen draw(_:)-Vorgang am Ende der Transaktion.
CATransaction.begin()
CATransaction.setDisableActions(true)
view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()
CATransaction.commit()
Verwenden Sie displayIfNeeded() nicht übermäßig — synchrones Neuzeichnen blockiert den Hauptthread und kann zu Bildaussetzern führen. Verwenden Sie es nur für Vorgänge, bei denen das Zeichenergebnis vor Ende des aktuellen Run Loops kritisch ist.
Häufig gestellte Fragen
Nein, der direkte Aufruf von draw(_:) ist verboten. setNeedsDisplay() markiert die View korrekt als dirty, und das System ruft draw(_:) zum richtigen Zeitpunkt des Renderzyklus auf. Der direkte Aufruf von draw(_:) ignoriert den Caching-Mechanismus und kann zu einem inkonsistenten Zustand führen.
Mehrere Aufrufe von setNeedsDisplay() für dieselbe View werden vom System zu einem einzigen draw(_:)-Aufruf zusammengefasst. Dies geschieht, weil die Methode nur das dirty-Flag setzt, das erst nach der tatsächlichen Ausführung von draw(_:) am Ende des Run Loops zurückgesetzt wird.
setNeedsDisplay() fordert ein Neuzeichnen des View-Inhalts an (Aufruf von draw(_:)). setNeedsLayout() fordert eine Neuanordnung der Subviews an (Aufruf von layoutSubviews()). layoutSubviews() kann zu Größenänderungen führen, die wiederum ein Neuzeichnen auslösen können.
setNeedsDisplay(_:) sollte für punktuelle Änderungen verwendet werden: Cursorbewegung, Farbänderung eines ausgewählten Bereichs, Aktualisierung eines Teils eines Diagramms. Dies begrenzt den Neuzeichnungsbereich und verbessert die Leistung im Vergleich zum Neuzeichnen der gesamten View.
Standardmäßig löst eine Änderung von frame oder bounds kein setNeedsDisplay() aus. Das System verschiebt die View nur. Wenn bei Größenänderungen ein Neuzeichnen erforderlich ist, setzen Sie die Eigenschaft contentMode auf .redraw — dann ruft UIKit bei jeder bounds-Änderung automatisch setNeedsDisplay() auf.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch