setNeedsDisplay() — är en instansmetod för UIView som markerar en vy som behöver omritning. Till skillnad från direkt anrop av draw(_:), sätter setNeedsDisplay() bara en dirty-flagga för vyn och återlämnar kontrollen omedelbart. Systemet behandlar denna begäran i närmaste renderingscykel, vilket gör det möjligt att slå samman flera anrop till en ritoperation. Enligt Apple Documentation (2025) är uppskjuten omritning via setNeedsDisplay() det enda korrekta sättet att uppdatera innehållet i en anpassad UIView.
Huvudpunkter
setNeedsDisplay() — är en UIView-metod som signalerar till systemet att vyens innehåll är föråldrat och kräver omrendering. Metoden tar inga parametrar och returnerar void. Efter anropet markeras vyn som dirty, och i närmaste ritcykel anropar systemet draw(_:) för denna vy.
Den viktigaste egenskapen är asynkronicitet. Flera på varandra följande anrop av setNeedsDisplay() för samma vy leder inte till flera anrop av draw(_:) — systemet slår samman dem till en operation. Detta är kritiskt för prestanda: om en vyegenskap ändras flera gånger per bildruta anropas draw(_:) endast en gång.
Mekanismen för uppskjuten omritning baseras på UIKit-renderingscykeln. När setNeedsDisplay() anropas sätter systemet den interna flaggan layer.needsDisplay till true. I slutet av den aktuella run loop går systemet igenom alla dirty-lager och anropar draw(_:) för varje.
UIKit run loop inkluderar update-fasen, under vilken dirty-vyer bearbetas. I denna fas anropas layoutSubviews() vid behov, därefter draw(_:) för alla markerade vyer. Efter ritningen överförs resultatet till GPU via Core Animation för kompositering.
class ProgressView: UIView {
var progress: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let ctx = UIGraphicsGetCurrentContext() else { return }
// Bakgrund
ctx.setFillColor(UIColor.lightGray.cgColor)
ctx.fill(rect)
// Förloppsindikator
let fillRect = CGRect(x: rect.minX, y: rect.minY,
width: rect.width * progress, height: rect.height)
ctx.setFillColor(UIColor.systemGreen.cgColor)
ctx.fill(fillRect)
}
}
I detta exempel uppdaterar progressView progress, och varje setter anropar automatiskt setNeedsDisplay(). Systemet väntar på slutet av run loop och anropar draw(_:) med det aktuella värdet för progress. Om progress ändras tre gånger i en bildruta, körs draw(_:) endast en gång — med det sista värdet.
Metoden setNeedsDisplay(_:) accepterar en parameter rect av typen CGRect, som begränsar omritningsområdet. Detta gör det möjligt att endast rita om den ändrade delen av vyn, inte hela. Skillnaden i prestanda blir märkbar vid arbete med stora vyer där förändringar påverkar ett litet område.
| Egenskap | setNeedsDisplay() | setNeedsDisplay(_:) |
|---|---|---|
| Parameter | Nej | CGRect — omritningsområde |
| Omritningsområde | Hela vyns bounds | Endast angiven rektangel |
| Prestanda | Lägre vid stora vyer | Högre vid partiella ändringar |
| Parameter rect i draw(_:) | Hela bounds | Angiven rektangel |
| När använda | Ändring av hela innehållet | Punktvisa ändringar (markör, highlight) |
För komplex grafik rekommenderas att först rita hela scenen i en offscreen-kontext och sedan anropa setNeedsDisplay(_:) endast för det ändrade området. Detta är en standardteknik i ritapplikationer och grafiska redigerare på iOS.
displayIfNeeded() — är en synkron metod som omedelbart startar omritning av alla dirty-vyer i hierarkin, utan att vänta på slutet av run loop. Till skillnad från setNeedsDisplay() som bara sätter en flagga, tvingar displayIfNeeded() omedelbar körning av draw(_:).
Apple rekommenderar att använda displayIfNeeded() endast i extrema fall, när ritresultatet behövs omedelbart — till exempel före skapande av skärmdump eller vid synkron generering av bild för UIGraphicsImageRenderer. I vanliga scenarier är den asynkrona modellen via setNeedsDisplay() att föredra eftersom den inte blockerar huvudtråden.
// Asynkron — rekommenderas
someView.setNeedsDisplay()
// Synkron — endast vid behov
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) anropades omedelbart
Anrop av displayIfNeeded() utan föregående setNeedsDisplay() ger ingen effekt — metoden bearbetar endast vyer som redan markerats som dirty. Om ingen vy kräver omritning avslutas displayIfNeeded() utan att anropa draw(_:).
Frekventa anrop av setNeedsDisplay() kan minska prestandan, särskilt under animering. Låt oss titta på de viktigaste optimeringsstrategierna.
Istället för att anropa setNeedsDisplay() vid varje ändring av en enskild egenskap, samla ändringarna och anropa metoden en gång efter att alla uppdateringar har tillämpats. Till exempel vid ändring av färg, storlek och position för ett element — anropa setNeedsDisplay() en gång efter att alla egenskaper har ställts in.
Om en egenskap kan animeras via CALayer (backgroundColor, opacity, position, transform), använd Core Animation istället för draw(_:). CALayer-egenskaper uppdateras på GPU utan att anropa draw(_:), vilket ger 60 FPS utan CPU-belastning.
För att gruppera flera ändringar i en renderingsgenomgång, använd CATransaction. Detta gör det möjligt att slå samman flera setNeedsDisplay() till en draw(_:)-operation i slutet av transaktionen.
CATransaction.begin()
CATransaction.setDisableActions(true)
view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()
CATransaction.commit()
Missbruka inte displayIfNeeded() — synkron omritning blockerar huvudtråden och kan orsaka bildrutefall. Använd det endast för operationer där ritresultatet är kritiskt före slutet av den aktuella run loop.
Vanliga frågor
Nej, direkt anrop av draw(_:) är förbjudet. setNeedsDisplay() markerar vyn korrekt som dirty, och systemet anropar draw(_:) vid rätt tillfälle i renderingscykeln. Direkt anrop av draw(_:) ignorerar cachelagringsmekanismen och kan leda till inkonsekvent tillstånd.
Flera anrop av setNeedsDisplay() för samma vy slås samman av systemet till ett anrop av draw(_:). Detta beror på att metoden bara sätter dirty flag, som återställs först efter faktisk körning av draw(_:) i slutet av run loop.
setNeedsDisplay() begär omritning av vyens innehåll (anrop av draw(_:)). setNeedsLayout() begär omplacering av subviews (anrop av layoutSubviews()). layoutSubviews() kan leda till storleksändring, vilket i sin tur kan orsaka omritning.
setNeedsDisplay(_:) bör användas vid punktvisa ändringar: markörrörelse, färgändring av markerat område, uppdatering av del av graf. Detta begränsar omritningsområdet och ökar prestandan jämfört med full omritning av hela vyn.
Som standard utlöser ändring av frame eller bounds inte setNeedsDisplay(). Systemet flyttar bara vyn. Om omritning krävs vid storleksändring, ställ in egenskapen contentMode till värdet .redraw — då kommer UIKit automatiskt att anropa setNeedsDisplay() vid varje ändring av bounds.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också