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(_:) — 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ță.
Î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(_:).
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
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.
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(_:).
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.
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ă.
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: — 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 super | super.draw(rect) | [super drawRect:rect] |
| Context | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| Parametru rect | rect: CGRect | CGRect 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.
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.
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(_:).
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(_:).
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()
}
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.
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
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.
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().
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.
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.
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
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.
Citiți și