draw(_:) и drawRect — шта је то, позив и преоптерећење

Аутор: IT Sectr Објављено: 2026-07-20 Време читања: 7 мин

draw(_:) / drawRect — је метода класе UIView (у Swift-у) и њен аналог у Objective-C-у (drawRect:), која је одговорна за приказивање садржаја прегледа коришћењем Core Graphics-а. Систем аутоматски позива ову методу приликом првог појављивања прегледа на екрану и након позива setNeedsDisplay(). Према Apple Documentation (2025), draw(_:) је једина метода у којој програмер има приступ графичком контексту тренутног екрана за прилагођено цртање. Преоптерећење draw(_:) даје потпуну контролу над изгледом компоненте — од једноставних геометријских облика до сложене анимиране графике.

Главно

  • draw(_:) — метода UIView-а за прилагођено цртање кроз Core Graphics, не позива се директно од стране програмера
  • drawRect: — еквивалент draw(_:) у Objective-C-у, синтаксички се разликује, функционално је идентичан
  • CGContext — графички контекст доступан унутар draw(_:) за све операције цртања
  • setNeedsDisplay() — једини исправан начин да се затражи поновни позив draw(_:) од стране програмера
  • UIGraphicsGetCurrentContext() — функција за добијање тренутног контекста унутар draw(_:), обавезан корак пре цртања

Шта је draw(_:) / drawRect

draw(_:) — је метода инстанце UIView-а коју UIKit позива за приказивање садржаја прегледа. Унутар ове методе програмер добија приступ графичком контексту CGContext и користи API Core Graphics-а за цртање линија, попуна, текста и слика. drawRect: у Objective-C-у обавља исту функцију, али са другачијом синтаксом: једини параметар је CGRect који одређује област за поновно цртање.

Према Apple Engineering (2024), draw(_:) ради кроз CPU-рендеринг Bitmap Graphics Context-а, што обезбеђује максималну флексибилност, али захтева више ресурса у поређењу са CALayer-ом. Одлука о коришћењу draw(_:) доноси се на основу сложености графике и захтева перформанси.

Потпис методе у Swift-у и Objective-C-у

У Swift-у метода се декларише као override func draw(_ rect: CGRect), где је rect правоугаоник који треба поново да се нацрта. У Objective-C-у потпис је - (void)drawRect:(CGRect)rect. Параметар rect може бити мањи од bounds прегледа при делимичном поновном цртању кроз setNeedsDisplay(_:).

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

Када систем позива draw(_:)

Систем аутоматски позива draw(_:) у строго одређеним сценаријима. Разумевање ових окидача помаже да се избегну непотребна поновна цртања и оптимизују перформансе прегледа. Испод су наведени сви случајеви аутоматског позива методе.

  • Прво приказивање — када се преглед први пут додаје у хијерархију и постаје видљив на екрану
  • setNeedsDisplay() — након позива ове методе од стране система у најближем циклусу цртања
  • setNeedsDisplay(_:) — исто, али са навођењем конкретног правоугаоника за поновно цртање
  • contentMode — при промени bounds, ако contentMode захтева поновно цртање (нпр. .redraw)
  • setNeedsLayout() — у неким случајевима након поновног распоређивања подпрегледа може бити потребно поновно цртање

Apple документација упозорава: никада не позивајте draw(_:) директно. Систем сам одлучује када да изврши цртање, а директни позив нарушава унутрашњи механизам кеширања. За захтев за поновним цртањем увек користите setNeedsDisplay() или setNeedsDisplay(_:).

Како преоптеретити draw(_:) у Swift-у

Преоптерећење draw(_:) у Swift-у почиње добијањем графичког контекста и накнадним позивима Core Graphics-а. Препоручује се креирање одвојених метода за логичке блокове цртања — то побољшава читљивост и тестираност кода.

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

    guard let context = UIGraphicsGetCurrentContext() else { return }

    // Параметри линије
    context.setStrokeColor(UIColor.darkGray.cgColor)
    context.setLineWidth(2.0)

    // Цртање троугла
    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()
}

У овом примеру draw(_:) црта троугао са тамно сивим контуром дебљине 2 пиксела. Позив super.draw(rect) на почетку методе препоручује Apple ради очувања родитељске логике цртања, иако је подразумевана имплементација draw(_:) у UIView-у празна.

Правило идемпотентности draw(_:)

Сваки позив draw(_:) треба да да идентичан резултат при истим улазним подацима. То омогућава систему да кешира резултат и не позива draw(_:) поново ако се садржај прегледа није променио. Немојте користити унутар draw(_:) случајне вредности, системско време или мрежне захтеве.

drawRect: у Objective-C-у и разлика са draw(_:)

drawRect: — историјски прва верзија методе, која се појавила у iOS 2.0 заједно са Objective-C-ом. У Swift-у метода је преименована у draw(_:) са коришћењем спољног параметра _. Функционално методе су идентичне: обе примају CGRect области поновног цртања и користе UIGraphicsGetCurrentContext() за приступ графичком контексту.

Карактеристикаdraw(_:) (Swift)drawRect: (Objective-C)
Потписoverride func draw(_ rect: CGRect)- (void)drawRect:(CGRect)rect
Позив supersuper.draw(rect)[super drawRect:rect]
КонтекстUIGraphicsGetCurrentContext()UIGraphicsGetCurrentContext()
Параметар rectrect: CGRectCGRect rect
ПерформансеИдентичнеИдентичне

При миграцији пројекта са Objective-C на Swift, преименовање методе је један од првих задатака. Xcode пружа аутоматски конвертер, али drawRect: захтева ручно ажурирање на draw(_:). Према Apple (2024), Swift верзија draw(_:) је пожељнија за нове пројекте.

Оптимизација цртања у draw(_:)

draw(_:) се извршава на CPU-у, а неоптимална имплементација може бити узрок пада фрејмова и ниских перформанси. Apple препоручује неколико проверених приступа за убрзање цртања.

Минимизирајте број операција цртања

Свака операција Core Graphics-а (move(to:), addLine(to:), strokePath) има додатне трошкове. Групишите операције и користите CGPath за сложене облике — путања се креира једном и поново користи при сваком позиву draw(_:).

Користите UIBezierPath за векторске објекте

UIBezierPath — је Objective-C омотач око CGPath-а који пружа једноставан API за креирање облика. Направите UIBezierPath унапред (нпр. у иницијализатору) и једноставно позовите fill() или stroke() унутар draw(_:).

swift
private let starPath: UIBezierPath = {
    let path = UIBezierPath()
    // Изградња облика звезде
    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()
}

Типичне грешке при раду са draw(_:)

Програмери често праве исте грешке при преоптерећењу draw(_:). Познавање ових образаца помаже да се избегну грешке и смањење перформанси. Размотримо најчешће проблеме и њихова решења.

  • Директан позив draw(_:) — никада не позивајте draw(_:) директно. Користите setNeedsDisplay() за захтев поновног цртања. Директан позив квари кеширање и може довести до нетачног приказа.
  • Тешка израчунавања унутар draw(_:) — draw(_:) треба да буде максимално лаган. Креирајте UIBezierPath, слике и друге тешке објекте изван методе, иницијализујући их једном.
  • Креирање објеката унутар draw(_:) — конструкције UIColor, UIFont и UIGraphicsImageRenderer унутар draw(_:) стварају додатно оптерећење на меморију. Преместите креирање објеката у својства класе.
  • Игнорисање параметра rect — rect показује област која захтева поновно цртање. Цртање изван rect систем одбацује, али троши ресурсе. Проверите пресек са rect пре цртања.
  • Недостатак super.draw(rect) — иако је имплементација UIView-а празна, Apple препоручује позивање super.draw(rect) ради компатибилности са будућим променама UIKit-а.

Пратећи ова правила, обезбедићете стабилно и брзо цртање прилагођених прегледа у било ком iOS пројекту. Профилишите draw(_:) кроз Instruments (Core Animation) да бисте видели стварно време извршења и уска грла.

Често постављана питања

Може ли се draw(_:) позвати директно?

Не, директан позив draw(_:) је забрањен Apple документацијом. Систем сам управља циклусом цртања. За захтев поновног цртања користите setNeedsDisplay(), који исправно означава преглед као онај који захтева ажурирање у најближем циклусу рендеринга.

Чиме се drawRect: разликује од draw(_:)?

Функционално ове методе су идентичне. drawRect: се користи у Objective-C-у, draw(_:) — у Swift-у. Обе примају CGRect области поновног цртања и користе исти Core Graphics контекст кроз UIGraphicsGetCurrentContext().

Зашто се draw(_:) не позива за празан UIView?

Apple оптимизује рендеринг: ако UIView нема преоптерећен draw(_:), систем не креира битмап контекст за њега. То штеди меморију. Ако преоптерећење постоји, али се метода не позива — проверите да оквир прегледа није нула и да је преглед видљив.

Колико често систем позива draw(_:)?

Само када је потребно: прво рендеровање, након setNeedsDisplay(), при промени bounds са contentMode = .redraw. У статичном стању draw(_:) се не позива поново, што штеди ресурсе CPU-а и батерију.

Да ли треба позвати super.draw(rect) у Swift-у?

Apple препоручује позивање super.draw(rect) на почетку преоптерећене методе. Иако је тренутна имплементација UIView-а празна, super позив обезбеђује компатибилност са будућим верзијама UIKit-а и добра је пракса.

Резиме

  • draw(_:) / drawRect — главна метода цртања UIView-а у Swift-у и Objective-C-у, позива се аутоматски од стране система
  • CGContext — графички контекст Core Graphics-а доступан унутар draw(_:) кроз UIGraphicsGetCurrentContext()
  • setNeedsDisplay() — исправан начин захтева поновног цртања; директан позив draw(_:) је забрањен
  • Идемпотентност — draw(_:) треба да да идентичан резултат при истим улазним подацима за исправно кеширање
  • UIBezierPath — креирајте путање изван draw(_:) да бисте минимизирали оптерећење CPU-а при сваком позиву
  • Параметар rect — садржи област поновног цртања; користите intersection за оптимизацију — не цртајте изван његових граница
  • Профилисање — проверавајте перформансе draw(_:) кроз Instruments Core Animation за откривање спорих операција

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође