draw(_:) și drawRect — ce este, apelare și suprascriere

Autor: IT Sectr Publicat: 2026-07-20 Timp de citire: 7 min

draw(_:) / drawRect — este metoda clasei UIView (în Swift) și analogul său în Objective-C (drawRect:), care răspunde de redarea conținutului view-ului folosind Core Graphics. Sistemul apelează această metodă automat la prima apariție a view-ului pe ecran și după apelarea setNeedsDisplay(). Conform Apple Documentation (2025), draw(_:) este singura metodă în care dezvoltatorul are acces la contextul grafic al ecranului curent pentru desenare personalizată. Suprascrierea draw(_:) oferă control complet asupra aspectului componentului — de la figuri geometrice simple până la grafică animată complexă.

Principalele puncte

  • draw(_:) — metodă UIView pentru desenare personalizată prin Core Graphics, nu este apelată direct de dezvoltator
  • drawRect: — echivalentul lui draw(_:) în Objective-C, diferit sintactic, identic funcțional
  • CGContext — context grafic disponibil în interiorul draw(_:) pentru toate operațiile de desenare
  • setNeedsDisplay() — singura modalitate corectă de a solicita re-apelarea draw(_:) din partea dezvoltatorului
  • UIGraphicsGetCurrentContext() — funcția de obținere a contextului curent în interiorul draw(_:), pas obligatoriu înainte de desenare

Ce este draw(_:) / drawRect

draw(_:) — este o metodă de instanță UIView pe care UIKit o apelează pentru redarea conținutului view-ului. În interiorul acestei metode dezvoltatorul obține acces la contextul grafic CGContext și folosește API-ul Core Graphics pentru a desena linii, umpleri, text și imagini. drawRect: în Objective-C îndeplinește aceeași funcție, dar cu o sintaxă diferită: singurul parametru este CGRect care specifică zona de re-desenare.

Conform Apple Engineering (2024), draw(_:) funcționează prin CPU-rendering Bitmap Graphics Context, ceea ce asigură flexibilitate maximă, dar necesită mai multe resurse comparativ cu CALayer. Decizia de a folosi draw(_:) se ia în funcție de complexitatea graficii și cerințele de performanță.

Semnătura metodei în Swift și Objective-C

În Swift metoda este declarată ca override func draw(_ rect: CGRect), unde rect este dreptunghiul care trebuie re-desenat. În Objective-C semnătura este - (void)drawRect:(CGRect)rect. Parametrul rect poate fi mai mic decât bounds view-ului la re-desenarea parțială prin setNeedsDisplay(_:).

objective-c
- (void)drawRect:(CGRect)rect {
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
    CGContextFillRect(context, rect);
}

Când apelează sistemul draw(_:)

Sistemul apelează automat draw(_:) în scenarii strict definite. Înțelegerea acestor declanșatori ajută la evitarea re-desenărilor inutile și la optimizarea performanței view-ului. Mai jos sunt enumerate toate cazurile de apelare automată a metodei.

  • Prima redare — când view-ul este adăugat pentru prima dată în ierarhie și devine vizibil pe ecran
  • setNeedsDisplay() — după apelarea acestei metode de către sistem la cel mai apropiat ciclu de desenare
  • setNeedsDisplay(_:) — la fel, dar cu specificarea dreptunghiului concret pentru re-desenare
  • contentMode — la modificarea bounds, dacă contentMode necesită re-desenare (de exemplu, .redraw)
  • setNeedsLayout() — în unele cazuri după re-aranjarea subview-urilor poate fi necesară re-desenarea

Documentația Apple avertizează: nu apelați niciodată draw(_:) direct. Sistemul însuși decide când să execute desenarea, iar apelarea directă strică mecanismul intern de cache. Pentru a solicita re-desenarea, folosiți întotdeauna setNeedsDisplay() sau setNeedsDisplay(_:).

Cum să suprascriem draw(_:) în Swift

Suprascrierea draw(_:) în Swift începe cu obținerea contextului grafic și apelurile ulterioare Core Graphics. Se recomandă crearea de metode separate pentru blocurile logice de desenare — aceasta îmbunătățește lizibilitatea și testabilitatea codului.

swift
override func draw(_ rect: CGRect) {
    super.draw(rect)

    guard let context = UIGraphicsGetCurrentContext() else { return }

    // Parametrii liniei
    context.setStrokeColor(UIColor.darkGray.cgColor)
    context.setLineWidth(2.0)

    // Desenarea unui triunghi
    context.move(to: CGPoint(x: rect.midX, y: rect.minY + 10))
    context.addLine(to: CGPoint(x: rect.maxX - 10, y: rect.maxY - 10))
    context.addLine(to: CGPoint(x: rect.minX + 10, y: rect.maxY - 10))
    context.closePath()
    context.strokePath()
}

În acest exemplu draw(_:) desenează un triunghi cu contur gri-închis de 2 pixeli grosime. Apelarea super.draw(rect) la începutul metodei este recomandată de Apple pentru păstrarea logicii de desenare a părintelui, deși implementarea implicită a draw(_:) în UIView este goală.

Regula idempotenței în draw(_:)

Fiecare apel draw(_:) trebuie să dea un rezultat identic la aceleași date de intrare. Acest lucru permite sistemului să cache-uiască rezultatul și să nu re-apeleze draw(_:) dacă conținutul view-ului nu s-a schimbat. Nu utilizați în interiorul draw(_:) valori aleatorii, timp de sistem sau cereri de rețea.

drawRect: în Objective-C și diferența cu draw(_:)

drawRect: — prima variantă istorică a metodei, apărută în iOS 2.0 împreună cu Objective-C. În Swift metoda a fost redenumită în draw(_:) cu utilizarea parametrului extern _. Funcțional metodele sunt identice: ambele primesc CGRect al zonei de re-desenare și folosesc UIGraphicsGetCurrentContext() pentru acces la contextul grafic.

Caracteristicădraw(_:) (Swift)drawRect: (Objective-C)
Semnăturăoverride func draw(_ rect: CGRect)- (void)drawRect:(CGRect)rect
Apelare supersuper.draw(rect)[super drawRect:rect]
ContextUIGraphicsGetCurrentContext()UIGraphicsGetCurrentContext()
Parametru rectrect: CGRectCGRect rect
PerformanțăIdenticăIdentică

La migrarea proiectului din Objective-C în Swift, redenumirea metodei este una dintre primele sarcini. Xcode oferă un convertor automat, dar drawRect: necesită actualizare manuală la draw(_:). Conform Apple (2024), versiunea Swift draw(_:) este preferată pentru proiecte noi.

Optimizarea desenării în draw(_:)

draw(_:) se execută pe CPU, iar o implementare neoptimizată poate cauza drop de cadre și performanță scăzută. Apple recomandă câteva abordări dovedite pentru accelerarea desenării.

Minimizați numărul operațiilor de desenare

Fiecare operație Core Graphics (move(to:), addLine(to:), strokePath) are costuri suplimentare. Grupați operațiile și folosiți CGPath pentru figuri complexe — calea se creează o dată și se reutilizează la fiecare apel draw(_:).

Folosiți UIBezierPath pentru obiecte vectoriale

UIBezierPath — este un wrapper Objective-C peste CGPath care oferă un API simplu pentru crearea de figuri. Creați UIBezierPath în avans (de exemplu, în inițializator) și pur și simplu apelați fill() sau stroke() în interiorul draw(_:).

swift
private let starPath: UIBezierPath = {
    let path = UIBezierPath()
    // Construirea unei forme de stea
    path.move(to: CGPoint(x: 50, y: 0))
    for i in 1...5 {
        let angle = CGFloat(i) * 4 * CGFloat.pi / 5
        path.addLine(to: CGPoint(x: 50 + 40 * cos(angle),
                               y: 50 + 40 * sin(angle)))
    }
    path.close()
    return path
}()

override func draw(_ rect: CGRect) {
    UIColor.systemYellow.setFill()
    starPath.fill()
}

Erori tipice la lucrul cu draw(_:)

Dezvoltatorii fac adesea aceleași greșeli la suprascrierea draw(_:). Cunoașterea acestor modele ajută la evitarea bug-urilor și a scăderii performanței. Să examinăm cele mai frecvente probleme și soluțiile lor.

  • Apelarea directă a draw(_:) — nu apelați niciodată draw(_:) direct. Folosiți setNeedsDisplay() pentru a solicita re-desenarea. Apelarea directă strică cache-ul și poate duce la afișare incorectă.
  • Calcule grele în interiorul draw(_:) — draw(_:) trebuie să fie cât mai ușor posibil. Creați UIBezierPath, imagini și alte obiecte grele în afara metodei, inițializându-le o singură dată.
  • Crearea de obiecte în interiorul draw(_:) — construcțiile UIColor, UIFont și UIGraphicsImageRenderer în interiorul draw(_:) creează încărcare suplimentară pe memorie. Mutați crearea obiectelor în proprietățile clasei.
  • Ignorarea parametrului rect — rect indică zona care necesită re-desenare. Desenarea în afara rect este respinsă de sistem, dar consumă resurse. Verificați intersecția cu rect înainte de desenare.
  • Lipsa super.draw(rect) — deși implementarea UIView este goală, Apple recomandă apelarea super.draw(rect) pentru compatibilitate cu versiunile viitoare ale UIKit.

Urmând aceste reguli, veți asigura o desenare stabilă și rapidă a view-urilor personalizate în orice proiect iOS. Profilați draw(_:) prin Instruments (Core Animation) pentru a vedea timpul real de execuție și punctele slabe.

Întrebări frecvente

Se poate apela draw(_:) direct?

Nu, apelarea directă a draw(_:) este interzisă de documentația Apple. Sistemul însuși gestionează ciclul de desenare. Pentru a solicita re-desenarea, folosiți setNeedsDisplay(), care marchează corect view-ul ca necesitând actualizare în cel mai apropiat ciclu de randare.

Cu ce diferă drawRect: de draw(_:)?

Funcțional aceste metode sunt identice. drawRect: este folosit în Objective-C, draw(_:) — în Swift. Ambele primesc CGRect al zonei de re-desenare și folosesc același context Core Graphics prin UIGraphicsGetCurrentContext().

De ce draw(_:) nu este apelat pentru un UIView gol?

Apple optimizează randarea: dacă UIView nu are un draw(_:) suprascris, sistemul nu creează un context bitmap pentru acesta. Aceasta economisește memorie. Dacă suprascrierea există dar metoda nu este apelată — verificați că cadrul view-ului nu este zero și view-ul este vizibil.

Cât de des apelează sistemul draw(_:)?

Doar la nevoie: prima randare, după setNeedsDisplay(), la modificarea bounds cu contentMode = .redraw. În stare statică draw(_:) nu este re-apelat, ceea ce economisește resursele CPU și bateria.

Trebuie să apelăm super.draw(rect) în Swift?

Apple recomandă apelarea super.draw(rect) la începutul metodei suprascrise. Deși implementarea curentă a UIView este goală, apelul super asigură compatibilitatea cu versiunile viitoare ale UIKit și este o bună practică.

Rezumat

  • draw(_:) / drawRect — metoda principală de desenare UIView în Swift și Objective-C, apelată automat de sistem
  • CGContext — contextul grafic Core Graphics disponibil în draw(_:) prin UIGraphicsGetCurrentContext()
  • setNeedsDisplay() — modalitatea corectă de a solicita re-desenarea; apelarea directă a draw(_:) este interzisă
  • Idempotența — draw(_:) trebuie să dea același rezultat la aceleași date de intrare pentru o cache-corectă
  • UIBezierPath — creați căi în afara draw(_:) pentru a minimiza încărcarea CPU la fiecare apel
  • Parametrul rect — conține zona de re-desenare; folosiți intersection pentru optimizare — nu desenați în afara limitelor sale
  • Profilarea — verificați performanța draw(_:) prin Instruments Core Animation pentru a detecta operațiile lente

Vom dezvolta o aplicație mobilă la cheie

IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.

Discutați proiectul

Citiți și