setNeedsDisplay() — ez egy UIView példánymetódus, amely a nézetet újrarajzolandóként jelöli meg. Ellentétben a draw(_:) közvetlen hívásával, a setNeedsDisplay() csak beállítja a dirty jelzőt a nézethez, és azonnal visszaadja a vezérlést. A rendszer ezt a kérést a legközelebbi renderelési ciklusban dolgozza fel, lehetővé téve több hívás egyetlen rajzolási műveletbe történő egyesítését. Az Apple Documentation (2025) szerint a setNeedsDisplay()-en keresztüli elhalasztott újrarajzolás az egyetlen helyes módja az egyedi UIView tartalmának frissítésére.
Főbb pontok
setNeedsDisplay() — egy UIView metódus, amely jelzi a rendszernek, hogy a nézet tartalma elavult, és újrarenderelést igényel. A metódus nem fogad el paramétereket, és void-ot ad vissza. A hívás után a nézet dirty jelölést kap, és a legközelebbi rajzolási ciklusban a rendszer meghívja a draw(_:) metódust erre a nézetre.
A kulcsfontosságú jellemző az aszinkronitás. Több egymást követő setNeedsDisplay() hívás ugyanarra a nézetre nem eredményez többszörös draw(_:) hívást — a rendszer egyetlen műveletbe egyesíti azokat. Ez kritikus a teljesítmény szempontjából: ha a nézet tulajdonsága képkockánként többször változik, a draw(_:) csak egyszer kerül meghívásra.
Az elhalasztott újrarajzolás mechanizmusa a UIKit renderelési ciklusán alapul. Amikor a setNeedsDisplay() meghívásra kerül, a rendszer a belső layer.needsDisplay jelzőt true-ra állítja. Az aktuális run loop végén a rendszer végigmegy az összes dirty rétegen, és mindegyikhez meghívja a draw(_:) metódust.
A UIKit run loop tartalmaz egy update fázist, amelyben a dirty nézetek feldolgozásra kerülnek. Ebben a fázisban szükség esetén meghívásra kerül a layoutSubviews(), majd a draw(_:) az összes megjelölt nézethez. A rajzolás befejezése után az eredmény a Core Animation-en keresztül a GPU-ra kerül kompozitálásra.
class ProgressView: UIView {
var progress: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let ctx = UIGraphicsGetCurrentContext() else { return }
// Háttér
ctx.setFillColor(UIColor.lightGray.cgColor)
ctx.fill(rect)
// Folyamatjelző sáv
let fillRect = CGRect(x: rect.minX, y: rect.minY,
width: rect.width * progress, height: rect.height)
ctx.setFillColor(UIColor.systemGreen.cgColor)
ctx.fill(fillRect)
}
}
Ebben a példában a progressView frissíti a progress értéket, és minden setter automatikusan meghívja a setNeedsDisplay()-et. A rendszer megvárja a run loop végét, és meghívja a draw(_:) metódust az aktuális progress értékkel. Ha a progress háromszor változik egy képkockán belül, a draw(_:) csak egyszer hajtódik végre — az utolsó értékkel.
A setNeedsDisplay(_:) metódus egy rect paramétert fogad el (CGRect típusú), amely korlátozza az újrarajzolás területét. Ez lehetővé teszi a nézet csak a megváltozott részének újrarajzolását, nem a teljes egészét. A teljesítménybeli különbség nagy nézeteknél válik észrevehetővé, ahol a változtatások kis területet érintenek.
| Jellemző | setNeedsDisplay() | setNeedsDisplay(_:) |
|---|---|---|
| Paraméter | Nincs | CGRect — újrarajzolás területe |
| Újrarajzolás területe | Teljes bounds | Csak a megadott téglalap |
| Teljesítmény | Alacsonyabb nagy nézeteknél | Magasabb részleges változtatásoknál |
| rect paraméter a draw(_:)-ben | Teljes bounds | Megadott téglalap |
| Mikor használjuk | Teljes tartalom változása | Pontszerű változtatások (kurzor, highlight) |
Komplex grafikákhoz ajánlott először a teljes jelenetet egy offscreen kontextusban megrajzolni, majd a setNeedsDisplay(_:)-et csak a megváltozott területre meghívni. Ez szabványos technika a rajzoló alkalmazásokban és grafikus szerkesztőkben iOS-en.
displayIfNeeded() — egy szinkron metódus, amely azonnal elindítja az összes dirty nézet újrarajzolását a hierarchiában, anélkül, hogy megvárná a run loop végét. Ellentétben a setNeedsDisplay()-vel, amely csak jelzőt állít be, a displayIfNeeded() kikényszeríti a draw(_:) azonnali végrehajtását.
Az Apple a displayIfNeeded() használatát csak végső esetben ajánlja, amikor a rajzolás eredményére azonnal szükség van — például képernyőkép készítése előtt vagy UIGraphicsImageRenderer számára szinkron képgeneráláskor. Normál forgatókönyvekben az aszinkron modell a setNeedsDisplay()-en keresztül előnyösebb, mivel nem blokkolja a fő szálat.
// Aszinkron — ajánlott
someView.setNeedsDisplay()
// Szinkron — csak szükség esetén
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) azonnal meghívva
A displayIfNeeded() hívása előzetes setNeedsDisplay() nélkül nem jár hatással — a metódus csak a már dirty-ként megjelölt nézeteket dolgozza fel. Ha egyetlen nézet sem igényel újrarajzolást, a displayIfNeeded() a draw(_:) meghívása nélkül fejeződik be.
A gyakori setNeedsDisplay() hívások csökkenthetik a teljesítményt, különösen animáció során. Tekintsük át a fő optimalizálási stratégiákat.
Ahelyett, hogy minden egyes tulajdonság változásakor meghívná a setNeedsDisplay()-et, gyűjtse össze a változtatásokat, és hívja meg a metódust egyszer az összes frissítés alkalmazása után. Például egy elem színének, méretének és pozíciójának változtatásakor — hívja meg a setNeedsDisplay()-et egyszer az összes tulajdonság beállítása után.
Ha egy tulajdonság animálható CALayer-en keresztül (backgroundColor, opacity, position, transform), használjon Core Animation-t a draw(_:) helyett. A CALayer tulajdonságok a GPU-n frissülnek a draw(_:) meghívása nélkül, 60 FPS-t biztosítva CPU terhelés nélkül.
Több változtatás egy renderelési menetbe történő csoportosításához használja a CATransaction-t. Ez lehetővé teszi több setNeedsDisplay() egyesítését egyetlen draw(_:) műveletbe a tranzakció végén.
CATransaction.begin()
CATransaction.setDisableActions(true)
view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()
CATransaction.commit()
Ne éljen vissza a displayIfNeeded() használatával — a szinkron újrarajzolás blokkolja a fő szálat, és képkocka-kiesést okozhat. Csak olyan műveletekhez használja, ahol a rajzolás eredménye kritikus az aktuális run loop befejezése előtt.
Gyakran Ismételt Kérdések
Nem, a draw(_:) közvetlen meghívása tilos. A setNeedsDisplay() helyesen jelöli meg a nézetet dirty-ként, és a rendszer a renderelési ciklus megfelelő pillanatában hívja meg a draw(_:)-t. A draw(_:) közvetlen meghívása figyelmen kívül hagyja a gyorsítótárazási mechanizmust, és inkonzisztens állapothoz vezethet.
Több setNeedsDisplay() hívás ugyanarra a nézetre a rendszer által egyetlen draw(_:) hívásba egyesítésre kerül. Ez azért történik, mert a metódus csak a dirty flag-et állítja be, amely csak a draw(_:) tényleges végrehajtása után, a run loop végén kerül visszaállításra.
A setNeedsDisplay() a nézet tartalmának újrarajzolását kéri (draw(_:) hívás). A setNeedsLayout() a subview-k újraelrendezését kéri (layoutSubviews() hívás). A layoutSubviews() méretváltozáshoz vezethet, ami viszont újrarajzolást idézhet elő.
A setNeedsDisplay(_:)-et pontszerű változtatásoknál kell használni: kurzor mozgatása, kijelölt terület színének változtatása, grafikon részének frissítése. Ez korlátozza az újrarajzolás területét, és növeli a teljesítményt a teljes nézet teljes újrarajzolásához képest.
Alapértelmezés szerint a frame vagy bounds változása nem hívja meg a setNeedsDisplay()-et. A rendszer csak mozgatja a nézetet. Ha méretváltozáskor újrarajzolás szükséges, állítsa a contentMode tulajdonságot .redraw értékre — ekkor a UIKit automatikusan meghívja a setNeedsDisplay()-et minden bounds változásnál.
Összegzés
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is