draw(_:) / drawRect — це метод класу UIView (у Swift) та його аналог в Objective-C (drawRect:), який відповідає за відображення вмісту в'ю за допомогою Core Graphics. Система викликає цей метод автоматично при першій появі в'ю на екрані та після виклику setNeedsDisplay(). Згідно з Apple Documentation (2025), draw(_:) є єдиним методом, де розробнику доступний графічний контекст поточного екрана для кастомного малювання. Перевизначення draw(_:) дає повний контроль над зовнішнім виглядом компонента — від простих геометричних фігур до складної анімованої графіки.
Головне
draw(_:) — це метод екземпляра UIView, який UIKit викликає для рендерингу вмісту в'ю. Всередині цього методу розробник отримує доступ до графічного контексту CGContext та використовує API Core Graphics для малювання ліній, заливок, тексту та зображень. drawRect: в Objective-C виконує ту саму функцію, але з іншим синтаксисом: єдиний параметр — CGRect, що задає область перемалювання.
За даними Apple Engineering (2024), draw(_:) працює через CPU-рендеринг Bitmap Graphics Context, що забезпечує максимальну гнучкість, але потребує більше ресурсів порівняно з CALayer. Рішення про використання draw(_:) приймається виходячи зі складності графіки та вимог до продуктивності.
У Swift метод оголошується як override func draw(_ rect: CGRect), де rect — прямокутник, який потрібно перемалювати. В Objective-C сигнатура - (void)drawRect:(CGRect)rect. Параметр rect може бути меншим за bounds в'ю при частковому перемалюванні через setNeedsDisplay(_:).
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, [UIColor redColor].CGColor);
CGContextFillRect(context, rect);
}
Система автоматично викликає draw(_:) у строго визначених сценаріях. Розуміння цих тригерів допомагає уникнути зайвих перемалювань та оптимізувати продуктивність в'ю. Нижче перераховані всі випадки автоматичного виклику методу.
Документація Apple попереджає: ніколи не викликайте draw(_:) безпосередньо. Система сама вирішує, коли виконати малювання, і прямий виклик порушує внутрішній механізм кешування. Для запиту перемалювання завжди використовуйте setNeedsDisplay() або setNeedsDisplay(_:).
Перевизначення draw(_:) у Swift починається з отримання графічного контексту та наступних викликів Core Graphics. Рекомендується створювати окремі методи для логічних блоків малювання — це покращує читабельність і тестованість коду.
override func draw(_ rect: CGRect) {
super.draw(rect)
guard let context = UIGraphicsGetCurrentContext() else { return }
// Line parameters
context.setStrokeColor(UIColor.darkGray.cgColor)
context.setLineWidth(2.0)
// Drawing a triangle
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(_:) випадкові значення, системний час або мережеві запити.
drawRect: — історично перший варіант методу, що з'явився в iOS 2.0 разом з Objective-C. У Swift метод було перейменовано в draw(_:) з використанням зовнішнього параметра _. Функціонально методи ідентичні: обидва отримують CGRect області перемалювання та використовують UIGraphicsGetCurrentContext() для доступу до графічного контексту.
| Характеристика | draw(_:) (Swift) | drawRect: (Objective-C) |
|---|---|---|
| Сигнатура | override func draw(_ rect: CGRect) | - (void)drawRect:(CGRect)rect |
| Виклик super | super.draw(rect) | [super drawRect:rect] |
| Контекст | UIGraphicsGetCurrentContext() | UIGraphicsGetCurrentContext() |
| Параметр rect | rect: CGRect | CGRect rect |
| Продуктивність | Ідентично | Ідентично |
При міграції проекту з Objective-C на Swift перейменування методу — одне з перших завдань. Xcode надає автоматичний конвертер, але drawRect: потребує ручного оновлення до draw(_:). За даними Apple (2024), Swift-версія draw(_:) є кращою для нових проектів.
draw(_:) виконується на CPU, і неоптимальна реалізація може стати причиною падіння кадрів та низької продуктивності. Apple рекомендує кілька перевірених підходів для прискорення малювання.
Кожна операція Core Graphics (move(to:), addLine(to:), strokePath) має накладні витрати. Групуйте операції та використовуйте CGPath для складних фігур — шлях створюється один раз і перевикористовується при кожному виклику draw(_:).
UIBezierPath — це Objective-C обгортка над CGPath, яка надає простий API для створення фігур. Створюйте UIBezierPath заздалегідь (наприклад, в ініціалізаторі) та просто викликайте fill() або stroke() всередині draw(_:).
private let starPath: UIBezierPath = {
let path = UIBezierPath()
// Building a star shape
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(_:). Знання цих патернів допомагає уникнути багів та зниження продуктивності. Розглянемо найпоширеніші проблеми та їх вирішення.
Дотримуючись цих правил, ви забезпечите стабільне та швидке малювання кастомних в'ю в будь-якому проекті під iOS. Профілюйте draw(_:) через Instruments (Core Animation), щоб побачити реальний час виконання та вузькі місця.
Часто задавані питання
Ні, прямий виклик draw(_:) заборонений документацією Apple. Система сама керує циклом малювання. Для запиту перемалювання використовуйте setNeedsDisplay(), який коректно позначає в'ю як таку, що потребує оновлення в найближчому циклі рендерингу.
Функціонально ці методи ідентичні. drawRect: використовується в Objective-C, draw(_:) — у Swift. Обидва отримують CGRect області перемалювання та використовують один і той самий контекст Core Graphics через UIGraphicsGetCurrentContext().
Apple оптимізує рендеринг: якщо у UIView немає перевизначеного draw(_:), система не створює для нього bitmap контекст. Це економить пам'ять. Якщо перевизначення є, але метод не викликається — перевірте, що фрейм в'ю не нульовий і в'ю видима.
Тільки при необхідності: перший рендеринг, після setNeedsDisplay(), при зміні bounds з contentMode = .redraw. У статичному стані draw(_:) не викликається повторно, що економить ресурси CPU та батареї.
Apple рекомендує викликати super.draw(rect) на початку перевизначеного методу. Хоча поточна реалізація UIView порожня, super-виклик забезпечує сумісність з майбутніми версіями UIKit і є хорошою практикою.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також