setNeedsDisplay() — dit is een instantiemethode van UIView die een weergave markeert als opnieuw getekend moet worden. In tegenstelling tot een directe aanroep van draw(_:), stelt setNeedsDisplay() alleen een dirty-vlag in voor de weergave en geeft de controle onmiddellijk terug. Het systeem verwerkt dit verzoek in de dichtstbijzijnde rendercyclus, waardoor meerdere aanroepen kunnen worden samengevoegd in één tekenbewerking. Volgens Apple Documentation (2025) is uitgestelde hertekening via setNeedsDisplay() de enige correcte manier om de inhoud van een aangepaste UIView bij te werken.
Belangrijkste punten
setNeedsDisplay() — is een UIView-methode die het systeem signaleert dat de inhoud van de weergave verouderd is en opnieuw gerenderd moet worden. De methode accepteert geen parameters en retourneert void. Na aanroep wordt de weergave gemarkeerd als dirty, en in de dichtstbijzijnde tekencyclus roept het systeem draw(_:) aan voor deze weergave.
Het belangrijkste kenmerk is asynchroniteit. Meerdere opeenvolgende aanroepen van setNeedsDisplay() voor dezelfde weergave leiden niet tot meerdere aanroepen van draw(_:) — het systeem voegt ze samen in één bewerking. Dit is cruciaal voor prestaties: als een eigenschap van de weergave meerdere keren per frame verandert, wordt draw(_:) slechts één keer aangeroepen.
Het mechanisme van uitgestelde hertekening is gebaseerd op de UIKit-rendercyclus. Wanneer setNeedsDisplay() wordt aangeroepen, stelt het systeem de interne vlag layer.needsDisplay in op true. Aan het einde van de huidige run loop doorloopt het systeem alle dirty-lagen en roept draw(_:) aan voor elke laag.
De UIKit-run loop omvat de update-fase, waarin dirty-weergaven worden verwerkt. In deze fase wordt layoutSubviews() indien nodig aangeroepen, gevolgd door draw(_:) voor alle gemarkeerde weergaven. Na voltooiing van het tekenen wordt het resultaat via Core Animation naar de GPU gestuurd voor compositie.
class ProgressView: UIView {
var progress: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let ctx = UIGraphicsGetCurrentContext() else { return }
// Achtergrond
ctx.setFillColor(UIColor.lightGray.cgColor)
ctx.fill(rect)
// Voortgangsbalk
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 dit voorbeeld werkt progressView progress bij, en elke setter roept automatisch setNeedsDisplay() aan. Het systeem wacht op het einde van de run loop en roept draw(_:) aan met de huidige waarde van progress. Als progress drie keer per frame verandert, wordt draw(_:) slechts één keer uitgevoerd — met de laatste waarde.
De methode setNeedsDisplay(_:) accepteert een parameter rect van het type CGRect, die het hertekeningsgebied beperkt. Dit maakt het mogelijk om alleen het gewijzigde deel van de weergave opnieuw te tekenen, niet het geheel. Het prestatieverschil wordt merkbaar bij het werken met grote weergaven waar wijzigingen een klein gebied betreffen.
| Kenmerk | setNeedsDisplay() | setNeedsDisplay(_:) |
|---|---|---|
| Parameter | Geen | CGRect — hertekeningsgebied |
| Hertekeningsgebied | Gehele bounds van weergave | Alleen opgegeven rechthoek |
| Prestaties | Lager bij grote weergaven | Hoger bij gedeeltelijke wijzigingen |
| Parameter rect in draw(_:) | Gehele bounds | Opgegeven rechthoek |
| Wanneer gebruiken | Wijziging van hele inhoud | Puntsgewijze wijzigingen (cursor, highlight) |
Voor complexe grafische afbeeldingen wordt aanbevolen eerst de hele scène in een offscreen-context te tekenen en vervolgens setNeedsDisplay(_:) alleen aan te roepen voor het gewijzigde gebied. Dit is een standaardtechniek in tekenapplicaties en grafische editors op iOS.
displayIfNeeded() — is een synchrone methode die onmiddellijk de hertekening start van alle dirty-weergaven in de hiërarchie, zonder te wachten op het einde van de run loop. In tegenstelling tot setNeedsDisplay(), die alleen een vlag instelt, dwingt displayIfNeeded() de onmiddellijke uitvoering van draw(_:) af.
Apple raadt aan displayIfNeeded() alleen in uiterste gevallen te gebruiken, wanneer het tekenresultaat onmiddellijk nodig is — bijvoorbeeld vóór het maken van een schermafbeelding of bij synchrone generatie van een afbeelding voor UIGraphicsImageRenderer. In normale scenario's heeft het asynchrone model via setNeedsDisplay() de voorkeur omdat het de hoofdthread niet blokkeert.
// Asynchroon — aanbevolen
someView.setNeedsDisplay()
// Synchroon — alleen wanneer nodig
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) onmiddellijk aangeroepen
Een aanroep van displayIfNeeded() zonder voorafgaand setNeedsDisplay() heeft geen effect — de methode verwerkt alleen weergaven die al als dirty zijn gemarkeerd. Als geen enkele weergave hertekening nodig heeft, wordt displayIfNeeded() beëindigd zonder draw(_:) aan te roepen.
Frequente aanroepen van setNeedsDisplay() kunnen de prestaties verminderen, vooral tijdens animatie. Laten we de belangrijkste optimalisatiestrategieën bekijken.
Roep setNeedsDisplay() niet aan bij elke wijziging van een afzonderlijke eigenschap, maar verzamel wijzigingen en roep de methode één keer aan na het toepassen van alle updates. Bijvoorbeeld bij het wijzigen van kleur, grootte en positie van een element — roep setNeedsDisplay() één keer aan na het instellen van alle eigenschappen.
Als een eigenschap kan worden geanimeerd via CALayer (backgroundColor, opacity, position, transform), gebruik dan Core Animation in plaats van draw(_:). CALayer-eigenschappen worden op de GPU bijgewerkt zonder draw(_:) aan te roepen, wat 60 FPS oplevert zonder CPU-belasting.
Gebruik CATransaction om meerdere wijzigingen te groeperen in één renderdoorgang. Dit maakt het mogelijk meerdere setNeedsDisplay()-aanroepen samen te voegen in één draw(_:)-bewerking aan het einde van de transactie.
CATransaction.begin()
CATransaction.setDisableActions(true)
view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()
CATransaction.commit()
Misbruik displayIfNeeded() niet — synchrone hertekening blokkeert de main-thread en kan frameverlies veroorzaken. Gebruik het alleen voor bewerkingen waarbij het tekenresultaat kritiek is vóór het einde van de huidige run loop.
Veelgestelde vragen
Nee, directe aanroep van draw(_:) is verboden. setNeedsDisplay() markeert de weergave correct als dirty, en het systeem roept draw(_:) aan op het juiste moment in de rendercyclus. Directe aanroep van draw(_:) negeert het caching-mechanisme en kan leiden tot inconsistente toestand.
Meerdere aanroepen van setNeedsDisplay() voor dezelfde weergave worden door het systeem samengevoegd in één aanroep van draw(_:). Dit gebeurt omdat de methode alleen de dirty flag instelt, die pas wordt gereset na de daadwerkelijke uitvoering van draw(_:) aan het einde van de run loop.
setNeedsDisplay() vraagt om hertekening van de weergave-inhoud (aanroep draw(_:)). setNeedsLayout() vraagt om herindeling van subviews (aanroep layoutSubviews()). layoutSubviews() kan leiden tot verandering van afmetingen, wat op zijn beurt hertekening kan veroorzaken.
setNeedsDisplay(_:) moet worden gebruikt bij puntsgewijze wijzigingen: cursorverplaatsing, kleurverandering van geselecteerd gebied, bijwerken van een deel van een grafiek. Dit beperkt het hertekeningsgebied en verhoogt de prestaties in vergelijking met volledige hertekening van de hele weergave.
Standaard veroorzaakt een wijziging van frame of bounds geen setNeedsDisplay(). Het systeem verplaatst alleen de weergave. Als hertekening nodig is bij groottewijziging, stel dan de eigenschap contentMode in op .redraw — dan roept UIKit automatisch setNeedsDisplay() aan bij elke verandering van bounds.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook