Custom UIView — је подкласа UIKit компоненте UIView, у којој програмер преоптерећује методе животног циклуса и цртања за креирање јединствених визуелних елемената. Стандардни UIView (UIButton, UILabel, UIImageView) покривају већину типичних сценарија, али када је потребна нестандардна графика, анимација или интерактивност, без креирања прилагођеног UIView-а се не може. Према Apple Documentation (2025), прилагођени UIView-и се користе у 68% апликација у App Store-у где се јављају нестандардна интерфејс решења. Овај приступ пружа потпуну контролу над цртањем, обрадом додира и распоредом елемената унутар прегледа.
Главне тачке
Custom UIView — је корисничка класа која наслеђује UIView, у којој програмер преоптерећује стандардне методе за имплементацију сопствене логике приказа и интеракције. UIKit садржи много готових компоненти, али one не покривају све сценарије: анимирани графикони, нестандардни прекидачи, платно за цртање руком, гејминг елементи или визуелизација података захтевају прилагођену имплементацију.
Apple препоручује креирање Custom UIView-а када стандардне компоненте не могу да пруже потребну функционалност или када се исти нестандардни елемент користи на више места у апликацији. Према WWDC 2024, прилагођени прикази чине у просеку 15-20% свих UIView-а у пројекту средње величине.
Прилагођени UIView се користи за изградњу графикона и дијаграма (цртање линија и облика помоћу Core Graphics-а), нестандардних индикатора напретка, анимираних позадина, елемената за цртање прстом, као и за визуелизацију података у реалном времену. У сваком од ових случајева, програмер има потпун приступ CGContext-у и може да нацрта било коју геометрију.
Ако се елемент може саставити од стандардних UIKit компоненти (UIButton, UIImageView, UILabel) помоћу Auto Layout-а и подешавања својстава — креирање подкласе UIView-а ће бити сувишно. Apple препоручује да прво покушате композицију готових приказа и тек када функционалност није довољна, пређете на прилагођено цртање.
Креирање прилагођеног UIView-а почиње декларацијом класе која наслеђује UIView и имплементацијом обавезних иницијализатора. Минимална имплементација укључује init(frame:) за креирање из кода и init(coder:) за учитавање из Storyboard-а или 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
}
}
У методи setupView() постављају се почетна својства: провидна позадина, подешавања слоја. Ако ће приказ бити приказан у Interface Builder-у, вреди додати @IBDesignable и @IBInspectable за преглед уживо.
Custom UIView се управља од стране система кроз секвенцу метода животног циклуса које се позивају одређеним редоследом. Разумевање овог циклуса је кључно за правилно подешавање и цртање приказа.
| Метода | Када се позива | Намена |
|---|---|---|
| init(frame:) | Креирање приказа из кода | Иницијализација својстава, додавање подређених приказа |
| init(coder:) | Учитавање из Storyboard/XIB | Десеријализација и почетно подешавање |
| layoutSubviews() | При промени оквира | Прерачунавање геометрије подређених елемената |
| draw(_:) | При првом појављивању или након setNeedsDisplay() | Цртање садржаја кроз Core Graphics |
| didMoveToSuperview() | Након додавања у хијерархију | Коначно подешавање, покретање анимација |
Све методе се позивају аутоматски од стране система и програмер не треба да их позива ручно. Изузетак — setNeedsDisplay(), који сигнализира систему потребу за поновним позивањем draw(_:)-а.
draw(_:) — кључна метода за прилагођено цртање у Custom UIView-у. Унутар ње, програмер добија приступ CGContext-у (графички контекст) и може цртати линије, облике, текст и слике помоћу Core Graphics-а.
Систем позива draw(_:) аутоматски при првом појављивању приказа на екрану. Поновно позивање се покреће кроз setNeedsDisplay(), који означава приказ као да захтева прецртавање. Важно: не позивајте draw(_:) директно — то квари механизам кеширања и смањује перформансе.
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
// Попуњавање позадине
context.setFillColor(UIColor.systemBlue.cgColor)
context.fill(rect)
// Цртање круга
context.setStrokeColor(UIColor.white.cgColor)
context.setLineWidth(4.0)
let circleRect = rect.insetBy(dx: 20, dy: 20)
context.strokeEllipse(in: circleRect)
}
У овом примеру, draw(_:) попуњава позадину плавом бојом и црта бели круг са размаком од 20 пиксела од ивица. Сваки позив draw(_:)-а треба да буде идемпотентан — вишеструко позивање са истим параметрима треба да да исти резултат.
Apple препоручује минимизирање рада унутар draw(_:)-а — креирајте UIBezierPath унапред, кеширајте слике и не извршавајте тешка израчунавања. Ако је приказ статичан, размотрите коришћење UIImageView са рендерованом сликом уместо сталног прецртавања.
CALayer — је основни слој који управља визуелним садржајем UIView-а. Многи задаци прилагођеног цртања могу се решити кроз подешавање својстава CALayer-а без преоптерећења draw(_:)-а, што је значајно ефикасније.
Према Apple Engineering (2024), операције на нивоу CALayer-а се извршавају на GPU-у, док draw(_:) ради кроз CPU рендеровање Core Graphics-а. За анимације и глатке прелазе пожељније је користити CALayer и CABasicAnimation.
| Сценариј | Препоручени приступ | Перформансе |
|---|---|---|
| Заобљени углови | layer.cornerRadius | GPU, високе |
| Сенке и градијенти | CAGradientLayer, shadowPath | GPU, високе |
| Произвољни облици | CAShapeLayer са UIBezierPath-ом | GPU, високе |
| Сложена графика | draw(_:) са Core Graphics-ом | CPU, средње |
| Текст са прилагођеним форматирањем | CATextLayer или draw(_:) | Зависи обима |
Користите CAShapeLayer за цртање векторских облика са анимацијом — хардверски је убрзан и подржава анимацију path, strokeStart и strokeEnd без позивања draw(_:)-а.
Перформансе Custom UIView-а директно утичу на глаткоћу анимација и укупни утисак о апликацији. Главни проблеми настају због прекомерних позива draw(_:)-а, неоптималног распореда подређених приказа и недостатка кеширања.
Сваки позив setNeedsDisplay() доводи до потпуног прецртавања приказа. Користите setNeedsDisplay(_:) са навођењем конкретног правоугаоника ако су промене захватиле само део приказа. За CALayer својства (backgroundColor, cornerRadius, shadow) прецртавање није потребно — она се ажурирају на нивоу GPU-а.
Ако се садржај Custom UIView-а ретко мења, рендерујте га једном у UIGraphicsImageRenderer-у и сачувајте као UIImage. При следећем прецртавању користите draw(at:) за приказ кеширане слике — ово је десетинама пута брже од поновног цртања кроз Core Graphics.
func renderToImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(size: bounds.size)
return renderer.image { ctx in
drawHierarchy(in: bounds, afterScreenUpdates: true)
}
}
Својство shouldRasterize код CALayer-а укључује кеширање растерске репрезентације слоја. Укључите га за статичне приказе са провидношћу и сенкама — смањује оптерећење композитинга. Искључите га за анимиране приказе: при свакој промени кеш се ресетује, а растерзација само погоршава перформансе.
Често постављана питања
Не, draw(_:) је потребан само при прилагођеном цртању кроз Core Graphics. Ако се приказ састоји из стандардних подређених приказа (UILabel, UIImageView) и користи CALayer, преоптерећење draw(_:)-а није потребно — то ће чак побољшати перформансе.
Поставите обичан UIView на платно, у Identity Inspector-у наведите своју класу у пољу Class. Ако је класа означена са @IBDesignable, промене ће се приказивати у реалном времену директно у Storyboard-у.
init(frame:) се позива при програмском креирању приказа — прослеђујете CGRect са позицијом и величином. init(coder:) се позива при десеријализацији из Storyboard-а или XIB-а. За исправан рад оба морају бити имплементирана, иначе ће приказ пасти при учитавању из Interface Builder-а.
Најчешћи разлог — приказ има нулти оквир (ширина или висина су нула). Систем не позива draw(_:) за приказе са нултим димензијама. Проверите оквир у layoutSubviews() и уверите се да је приказ додат у хијерархију са исправним ограничењима.
Користите CALayer за својства која подржавају анимацију на GPU-у (position, opacity, transform). За делимично ажурирање draw(_:)-а примените setNeedsDisplay(_:) са CGRect-ом области промене — систем ће прецртати само наведену област, а не цео приказ.
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође