Custom UIView — este o subclasă a componentului UIKit UIView, în care dezvoltatorul suprascrie metodele ciclului de viață și de desenare pentru a crea elemente vizuale unice. UIView-urile standard (UIButton, UILabel, UIImageView) acoperă majoritatea scenariilor tipice, dar când este necesară o grafică, o animație sau o interactivitate personalizată, nu se poate fără crearea unui UIView personalizat. Conform Apple Documentation (2025), UIView-urile personalizate sunt utilizate în 68% din aplicațiile din App Store unde apar soluții de interfață personalizate. Această abordare oferă control complet asupra desenării, manipulării atingerilor și aranjării elementelor în interiorul vizualizării.
Puncte cheie
Custom UIView — este o clasă personalizată care moștenește UIView, în care dezvoltatorul suprascrie metodele standard pentru a implementa propria logică de afișare și interacțiune. UIKit conține multe componente gata făcute, dar nu acoperă toate scenariile: grafice animate, comutatoare personalizate, pânză pentru desenat de mână, elemente de joc sau vizualizare de date necesită implementare personalizată.
Apple recomandă crearea Custom UIView atunci când componentele standard nu pot oferi funcționalitatea necesară sau când același element personalizat este utilizat în mai multe locuri ale aplicației. Conform WWDC 2024, vizualizările personalizate reprezintă în medie 15-20% din totalul UIView-urilor într-un proiect de dimensiune medie.
UIView personalizat este utilizat pentru construirea de grafice și diagrame (desenarea liniilor și formelor cu Core Graphics), indicatori de progres personalizați, fundaluri animate, elemente pentru desenat cu degetul, precum și pentru vizualizarea datelor în timp real. În fiecare dintre aceste cazuri, dezvoltatorul are acces complet la CGContext și poate desena orice geometrie.
Dacă elementul poate fi asamblat din componente standard UIKit (UIButton, UIImageView, UILabel) folosind Auto Layout și configurarea proprietăților — crearea unei subclase UIView va fi de prisos. Apple recomandă să încercați mai întâi compoziția vizualizărilor gata făcute și numai când funcționalitatea este insuficientă să treceți la desenarea personalizată.
Crearea unui UIView personalizat începe cu declararea unei clase care moștenește UIView și implementarea inițializatorilor obligatorii. Implementarea minimă include init(frame:) pentru crearea din cod și init(coder:) pentru încărcarea din Storyboard sau XIB.
import UIKit
class CircleView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
private func setupView() {
backgroundColor = .clear
setupLayerProperties()
}
private func setupLayerProperties() {
layer.cornerRadius = bounds.width / 2
layer.masksToBounds = true
}
}
În metoda setupView() se stabilesc proprietățile inițiale: fundal transparent, setări ale stratului. Dacă vizualizarea va fi afișată în Interface Builder, merită adăugate @IBDesignable și @IBInspectable pentru previzualizare live.
Custom UIView este gestionat de sistem printr-o succesiune de metode ale ciclului de viață care sunt apelate într-o anumită ordine. Înțelegerea acestui ciclu este crucială pentru configurarea și desenarea corectă a vizualizării.
| Metodă | Când este apelată | Scop |
|---|---|---|
| init(frame:) | Crearea vizualizării din cod | Inițializarea proprietăților, adăugarea subview-urilor |
| init(coder:) | Încărcarea din Storyboard/XIB | Deserializarea și configurarea inițială |
| layoutSubviews() | La modificarea frame-ului | Recalcularea geometriei elementelor copil |
| draw(_:) | La prima apariție sau după setNeedsDisplay() | Desenarea conținutului prin Core Graphics |
| didMoveToSuperview() | După adăugarea în ierarhie | Configurarea finală, pornirea animațiilor |
Toate metodele sunt apelate automat de sistem și dezvoltatorul nu trebuie să le apeleze manual. Excepție — setNeedsDisplay(), care semnalează sistemului necesitatea reapelării lui draw(_:).
draw(_:) — metoda cheie pentru desenarea personalizată în Custom UIView. În interiorul ei, dezvoltatorul obține acces la CGContext (contextul grafic) și poate desena linii, forme, text și imagini folosind Core Graphics.
Sistemul apelează draw(_:) automat la prima apariție a vizualizării pe ecran. Reapelarea este inițiată prin setNeedsDisplay(), care marchează vizualizarea ca necesitând redesenare. Important: nu apelați draw(_:) direct — aceasta strică mecanismul de cache și reduce performanța.
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
// Umplerea fundalului
context.setFillColor(UIColor.systemBlue.cgColor)
context.fill(rect)
// Desenarea unui cerc
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(4.0)
let circleRect = rect.insetBy(dx: 20, dy: 20)
context.strokeEllipse(in: circleRect)
}
În acest exemplu, draw(_:) umple fundalul cu culoare albastră și desenează un cerc alb cu o margine de 20 pixeli de la margini. Fiecare apel draw(_:) trebuie să fie idempotent — apelarea repetată cu aceiași parametri trebuie să dea același rezultat.
Apple recomandă minimizarea muncii în interiorul draw(_:) — creați UIBezierPath în avans, stocați în cache imaginile și nu executați calcule grele. Dacă vizualizarea este statică, luați în considerare utilizarea UIImageView cu o imagine redată în locul redesenării constante.
CALayer — este stratul subiacent care gestionează conținutul vizual al UIView. Multe sarcini de desenare personalizată pot fi rezolvate prin configurarea proprietăților CALayer fără a suprascrie draw(_:), ceea ce este semnificativ mai performant.
Conform Apple Engineering (2024), operațiile la nivelul CALayer se execută pe GPU, în timp ce draw(_:) funcționează prin randare CPU Core Graphics. Pentru animații și tranziții fluide este preferabilă utilizarea CALayer și CABasicAnimation.
| Scenariu | Abordare recomandată | Performanță |
|---|---|---|
| Colțuri rotunjite | layer.cornerRadius | GPU, mare |
| Umbre și gradiente | CAGradientLayer, shadowPath | GPU, mare |
| Forme arbitrare | CAShapeLayer cu UIBezierPath | GPU, mare |
| Grafică complexă | draw(_:) cu Core Graphics | CPU, medie |
| Text cu formatare personalizată | CATextLayer sau draw(_:) | Depinde de volum |
Utilizați CAShapeLayer pentru desenarea formelor vectoriale cu animație — este accelerat hardware și suportă animația path, strokeStart și strokeEnd fără a apela draw(_:).
Performanța Custom UIView influențează direct fluiditatea animațiilor și impresia generală a aplicației. Problemele principale apar din cauza apelurilor excesive ale draw(_:), a aranjării neoptimale a subview-urilor și a lipsei de cache.
Fiecare apel setNeedsDisplay() duce la redesenarea completă a vizualizării. Utilizați setNeedsDisplay(_:) cu specificarea unui dreptunghi concret, dacă modificările au afectat doar o parte a vizualizării. Pentru proprietățile CALayer (backgroundColor, cornerRadius, shadow) redesenarea nu este necesară — acestea se actualizează la nivel GPU.
Dacă conținutul Custom UIView se schimbă rar, randați-l o dată în UIGraphicsImageRenderer și salvați-l ca UIImage. La următoarea redesenare, utilizați draw(at:) pentru a afișa imaginea din cache — aceasta este de zeci de ori mai rapid decât redesenarea prin Core Graphics.
func renderToImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(size: bounds.size)
return renderer.image { ctx in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
}
Proprietatea shouldRasterize a CALayer activează stocarea în cache a reprezentării rasterizate a stratului. Activați-o pentru vizualizări statice cu transparență și umbre — reduce încărcarea compositing-ului. Dezactivați-o pentru vizualizări animate: la fiecare modificare cache-ul se resetează, iar rasterizarea doar înrăutățește performanța.
Întrebări frecvente
Nu, draw(_:) este necesar doar la desenarea personalizată prin Core Graphics. Dacă vizualizarea este alcătuită din subview-uri standard (UILabel, UIImageView) și utilizează CALayer, suprascrierea draw(_:) nu este necesară — aceasta chiar va îmbunătăți performanța.
Plasați un UIView obișnuit pe canvas, în Identity Inspector specificați clasa dvs. în câmpul Class. Dacă clasa este marcată cu @IBDesignable, modificările se vor afișa în timp real direct în Storyboard.
init(frame:) este apelat la crearea programatică a vizualizării — transmiteți un CGRect cu poziția și dimensiunea. init(coder:) este apelat la deserializarea din Storyboard sau XIB. Pentru funcționarea corectă, ambele trebuie implementate, altfel vizualizarea va da eroare la încărcarea din Interface Builder.
Cea mai frecventă cauză — vizualizarea are frame zero (lățimea sau înălțimea sunt zero). Sistemul nu apelează draw(_:) pentru vizualizări cu dimensiuni zero. Verificați frame-ul în layoutSubviews() și asigurați-vă că vizualizarea a fost adăugată în ierarhie cu constrângeri corecte.
Utilizați CALayer pentru proprietățile care suportă animația pe GPU (position, opacity, transform). Pentru actualizarea parțială a draw(_:) aplicați setNeedsDisplay(_:) cu CGRect al zonei de modificare — sistemul va redesena doar zona specificată, nu întreaga vizualizare.
Concluzii
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