setNeedsDisplay(): podstata metody a mechanismus překreslení v iOS

Autor: IT Sectr Publikováno: 2026-07-20 Doba čtení: 6 min

setNeedsDisplay() — je instanční metoda UIView, která označuje zobrazení jako vyžadující opětovné vykreslení. Na rozdíl od přímého volání draw(_:), setNeedsDisplay() pouze nastaví dirty příznak pro zobrazení a okamžitě vrátí řízení. Systém zpracuje tento požadavek v nejbližším renderovacím cyklu, což umožňuje sloučit několik volání do jedné operace kreslení. Podle Apple Documentation (2025) je odložené překreslení prostřednictvím setNeedsDisplay() jediným správným způsobem aktualizace obsahu vlastního UIView.

Hlavní body

  • setNeedsDisplay() — odložený požadavek na překreslení UIView, nevolá draw(_:) synchronně
  • dirty flag — vnitřní příznak zobrazení, který se nastavuje setNeedsDisplay() a resetuje po draw(_:)
  • setNeedsDisplay(_:) — přetížení s CGRect pro částečné překreslení pouze zadané oblasti
  • displayIfNeeded() — vynucené synchronní překreslení, používané pro okamžitou aktualizaci
  • Renderovací cyklus — systém zpracovává dirty zobrazení na konci run loop, slučuje několik požadavků do jednoho průchodu

Co je setNeedsDisplay()

setNeedsDisplay() — je metoda UIView, která signalizuje systému, že obsah zobrazení je zastaralý a vyžaduje nové vykreslení. Metoda nepřijímá parametry a vrací void. Po volání je zobrazení označeno jako dirty a v nejbližším kreslicím cyklu systém zavolá draw(_:) pro toto zobrazení.

Klíčovou vlastností je asynchronnost. Několik po sobě jdoucích volání setNeedsDisplay() pro stejné zobrazení nepovede k několikanásobným voláním draw(_:) — systém je sloučí do jedné operace. To je kriticky důležité pro výkon: pokud se vlastnost zobrazení změní několikrát za snímek, draw(_:) se volá pouze jednou.

Jak funguje odložené překreslení

Mechanismus odloženého překreslení je založen na renderovacím cyklu UIKit. Když je setNeedsDisplay() voláno, systém nastaví vnitřní příznak layer.needsDisplay na true. Na konci aktuálního run loop systém projde všechny dirty vrstvy a pro každou zavolá draw(_:).

Run loop a kroky renderování

UIKit run loop zahrnuje fázi update, během které se zpracovávají dirty zobrazení. V této fázi se v případě potřeby volá layoutSubviews(), poté draw(_:) pro všechna označená zobrazení. Po dokončení kreslení je výsledek předán na GPU přes Core Animation pro kompozici.

swift
class ProgressView: UIView {
    var progress: CGFloat = 0 {
        didSet {
            setNeedsDisplay()
        }
    }

    override func draw(_ rect: CGRect) {
        super.draw(rect)
        guard let ctx = UIGraphicsGetCurrentContext() else { return }

        // Pozadí
        ctx.setFillColor(UIColor.lightGray.cgColor)
        ctx.fill(rect)

        // Indikátor průběhu
        let fillRect = CGRect(x: rect.minX, y: rect.minY,
                              width: rect.width * progress, height: rect.height)
        ctx.setFillColor(UIColor.systemGreen.cgColor)
        ctx.fill(fillRect)
    }
}

V tomto příkladu progressView aktualizuje progress a každý setter automaticky volá setNeedsDisplay(). Systém počká na konec run loop a zavolá draw(_:) s aktuální hodnotou progress. Pokud se progress změní třikrát v jednom snímku, draw(_:) se provede pouze jednou — s poslední hodnotou.

setNeedsDisplay() vs setNeedsDisplay(_:)

Metoda setNeedsDisplay(_:) přijímá parametr rect typu CGRect, který omezuje oblast překreslení. To umožňuje překreslit pouze změněnou část zobrazení, nikoli celé. Rozdíl ve výkonu je patrný při práci s velkými zobrazeními, kde změny ovlivňují malou oblast.

VlastnostsetNeedsDisplay()setNeedsDisplay(_:)
ParametrNeCGRect — oblast překreslení
Oblast překresleníCelé bounds zobrazeníPouze zadaný obdélník
VýkonNižší u velkých zobrazeníVyšší u částečných změn
Parametr rect v draw(_:)Celé boundsZadaný obdélník
Kdy použítZměna celého obsahuBodové změny (kurzor, highlight)

Pro složitou grafiku se doporučuje nejprve nakreslit celou scénu v offscreen kontextu a poté volat setNeedsDisplay(_:) pouze pro změněnou oblast. To je standardní technika v kreslicích aplikacích a grafických editorech na iOS.

displayIfNeeded() proti setNeedsDisplay()

displayIfNeeded() — je synchronní metoda, která okamžitě spustí překreslení všech dirty zobrazení v hierarchii, aniž by čekala na konec run loop. Na rozdíl od setNeedsDisplay(), které pouze nastaví příznak, displayIfNeeded() vynucuje okamžité provedení draw(_:).

Apple doporučuje používat displayIfNeeded() pouze v krajních případech, kdy je výsledek kreslení potřeba okamžitě — například před vytvořením snímku obrazovky nebo při synchronním generování obrázku pro UIGraphicsImageRenderer. V běžných scénářích je asynchronní model přes setNeedsDisplay() preferován, protože neblokuje hlavní vlákno.

swift
    // Asynchronní — doporučeno
someView.setNeedsDisplay()

    // Synchronní — pouze v nezbytném případě
someView.setNeedsDisplay()
someView.displayIfNeeded() // draw(_:) voláno okamžitě

Volání displayIfNeeded() bez předchozího setNeedsDisplay() nebude mít účinek — metoda zpracovává pouze již označená dirty zobrazení. Pokud žádné zobrazení nevyžaduje překreslení, displayIfNeeded() skončí bez volání draw(_:).

Optimalizace volání setNeedsDisplay()

Častá volání setNeedsDisplay() mohou snížit výkon, zejména při animaci. Podívejme se na hlavní optimalizační strategie.

Slučování změn přes model

Místo volání setNeedsDisplay() při každé změně jednotlivé vlastnosti shromažďujte změny a volejte metodu jednou po aplikování všech aktualizací. Například při změně barvy, velikosti a pozice prvku — zavolejte setNeedsDisplay() jednou po nastavení všech vlastností.

Použití CALayer pro animovatelné vlastnosti

Pokud lze vlastnost animovat přes CALayer (backgroundColor, opacity, position, transform), použijte Core Animation místo draw(_:). Vlastnosti CALayer se aktualizují na GPU bez volání draw(_:), což poskytuje 60 FPS bez zatížení CPU.

Dávková aktualizace přes CATransaction

Pro seskupení několika změn do jednoho renderovacího průchodu použijte CATransaction. To umožňuje sloučit několik setNeedsDisplay() do jedné operace draw(_:) na konci transakce.

swift
CATransaction.begin()
CATransaction.setDisableActions(true)

view1.setNeedsDisplay()
view2.setNeedsDisplay()
view3.setNeedsDisplay()

CATransaction.commit()

Nezneužívejte displayIfNeeded() — synchronní překreslení blokuje hlavní vlákno a může způsobit ztrátu snímků. Používejte jej pouze pro operace, kde je výsledek kreslení kritický před dokončením aktuálního run loop.

Často kladené otázky

Lze volat draw(_:) přímo místo setNeedsDisplay()?

Ne, přímé volání draw(_:) je zakázáno. setNeedsDisplay() správně označí zobrazení jako dirty a systém zavolá draw(_:) ve správný okamžik renderovacího cyklu. Přímé volání draw(_:) ignoruje mechanismus ukládání do mezipaměti a může vést k nekonzistentnímu stavu.

Co se stane při vícenásobných voláních setNeedsDisplay()?

Několik volání setNeedsDisplay() pro stejné zobrazení je systémem sloučeno do jednoho volání draw(_:). To se děje proto, že metoda pouze nastaví dirty flag, který se resetuje až po skutečném provedení draw(_:) na konci run loop.

Čím se setNeedsDisplay() liší od setNeedsLayout()?

setNeedsDisplay() požaduje překreslení obsahu zobrazení (volání draw(_:)). setNeedsLayout() požaduje nové uspořádání subview (volání layoutSubviews()). layoutSubviews() může vést ke změně rozměrů, což zase může vyvolat překreslení.

Kdy použít setNeedsDisplay(_:)?

setNeedsDisplay(_:) by se mělo použít při bodových změnách: pohyb kurzoru, změna barvy vybrané oblasti, aktualizace části grafu. To omezuje oblast překreslení a zvyšuje výkon ve srovnání s úplným překreslením celého zobrazení.

Vyvolá změna frame automaticky setNeedsDisplay()?

Ve výchozím nastavení změna frame nebo bounds nevyvolá setNeedsDisplay(). Systém pouze přesune zobrazení. Pokud je vyžadováno překreslení při změně velikosti, nastavte vlastnost contentMode na hodnotu .redraw — pak UIKit bude automaticky volat setNeedsDisplay() při každé změně bounds.

Shrnutí

  • setNeedsDisplay() — asynchronní metoda UIView pro požadavek odloženého překreslení nastavením dirty flag
  • setNeedsDisplay(_:) — přetížení s CGRect pro částečné překreslení pouze zadané oblasti, optimalizuje výkon
  • displayIfNeeded() — synchronní metoda pro okamžité překreslení dirty zobrazení, používá se pouze při krajní potřebě
  • Run loop — systém zpracovává dirty zobrazení na konci aktuálního cyklu, slučuje vícenásobná volání do jednoho průchodu draw(_:)
  • CALayer — pro animovatelné vlastnosti (position, opacity) použijte Core Animation místo draw(_:) — to je řádově výkonnější
  • CATransaction — seskupuje několik změn do jednoho renderovacího cyklu, snižuje počet volání draw(_:)
  • contentMode = .redraw — zapíná automatické volání setNeedsDisplay() při změně bounds zobrazení

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také