Какова роль метода drawRect в кастомной UIView
Что проверяют
Знаете ли вы, как UIKit рисует содержимое view и во что обходится переопределение draw(_:). Следом обычно спрашивают, когда метод вызывается, почему его нельзя звать напрямую и чем заменить его для анимируемой графики.
Как отвечать
draw(_ rect: CGRect), в Objective-C drawRect:, это место, где view рисует своё содержимое через Core Graphics или UIKit. Перед вызовом UIKit сам готовит контекст, доступный через UIGraphicsGetCurrentContext(), а результат сохраняется в слое. Дальше система показывает готовую картинку, пока её не попросят перерисовать.
Главное про жизненный цикл:
- когда вызывается: при первом показе и после
setNeedsDisplay()илиsetNeedsDisplay(_:). Перерисовка откладывается до следующего цикла отрисовки, несколько запросов склеиваются в один вызов; - смена размера: по умолчанию
contentModeравен.scaleToFill, и при изменении bounds старая картинка просто растягивается. Чтобы метод вызывался заново, ставят.redraw; - параметр
rect: это грязная область, а не вся view. Рисуют относительноbounds, аrectиспользуют, чтобы не рисовать лишнего; super: прямому наследникуUIViewвызывать не нужно, наследнику другого класса view нужно;- поток: только главный, как и вся работа с UIKit.
Цена в том, что под view выделяется буфер размером в пиксели с учётом scale, обычно по 4 байта на пиксель, а рисует CPU. Поэтому пустой draw(_:) не оставляют: одно его наличие заставляет выделять буфер.
Альтернативы: CAShapeLayer и CAGradientLayer для фигур и градиентов, которые анимируются без перерисовки, UIGraphicsImageRenderer для заранее отрисованной картинки, в SwiftUI Shape и Canvas.
Где спотыкаются
Вызывают draw(_:) напрямую, чтобы обновить картинку. Контекста в этот момент нет, и рисовать некуда. Правильный путь это setNeedsDisplay().
Заливают rect вместо bounds. Пока перерисовывается вся view, разницы не видно, но при частичной перерисовке фигуры смещаются и обрезаются.
Анимируют через setNeedsDisplay() на каждом кадре. CPU перерисовывает весь буфер 60 или 120 раз в секунду, тогда как CAShapeLayer с анимацией strokeEnd делает то же силами Core Animation.