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 для сохранения родительской логики отрисовки, хотя у UIView реализация draw(_:) по умолчанию пуста.
Каждый вызов 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 контекст. Это экономит память. Если переопределение есть, но метод не вызывается — проверьте, что фрейм вью не нулевой и вью visible.
Только при необходимости: первый рендеринг, после setNeedsDisplay(), при изменении bounds с contentMode = .redraw. В статичном состоянии draw(_:) не вызывается повторно, что экономит ресурсы CPU и батарею.
Apple рекомендует вызывать super.draw(rect) в начале переопределённого метода. Хотя текущая реализация UIView пуста, super-вызов обеспечивает совместимость с будущими версиями UIKit и является хорошей практикой.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также