Используете ли вы drawLayer(_:inContext:)
или drawRect(_:)
(или оба) для пользовательского кода рисования, зависит от того, нужен ли вам доступ к текущему значению свойства слоя во время его анимации.
Сегодня я боролся с различными проблемами рендеринга, связанными с этими двумя функциями, при реализации моего собственного класса Label .После проверки документации, выполнения проб и ошибок, декомпиляции UIKit и изучения примера пользовательских анимируемых свойств Apple я понял, как он работает.
drawRect(_:)
Если вам не нужно получать доступ к текущему значению свойства слоя / вида во время его анимации, вы можете просто использовать drawRect(_:)
, чтобы выполнить свой собственный рисунок.Все будет отлично работать.
override func drawRect(rect: CGRect) {
// your custom drawing code
}
drawLayer(_:inContext:)
Скажем, например, что вы хотите использовать backgroundColor
в своем пользовательском коде рисования:
override func drawRect(rect: CGRect) {
let colorForCustomDrawing = self.layer.backgroundColor
// your custom drawing code
}
Когда вы протестируете свой код, вы заметите, что backgroundColor
не возвращает правильное (то есть текущее) значение, пока анимация находится в полете.Вместо этого он возвращает окончательное значение (т. Е. Значение для завершения анимации).
Чтобы получить значение current во время анимации, необходимо получить доступ к backgroundColor
layer
параметр передан в drawLayer(_:inContext:)
.И вы также должны нарисовать параметр context
.
Очень важно знать, что self.layer
представления и параметр layer
переданы в drawLayer(_:inContext:)
не всегда один и тот же слой!Последний может быть копией первого с частичной анимацией, уже примененной к его свойствам. Таким образом, вы можете получить доступ к правильным значениям свойств анимации в полете.
Теперь чертеж работает, как и ожидалось:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
let colorForCustomDrawing = layer.backgroundColor
// your custom drawing code
}
Но есть две новые проблемы: setNeedsDisplay()
и несколько свойств, таких как backgroundColor
и opaque
, больше не работают для вашего вида.UIView
больше не переадресовывает вызовы и не переключается на свой собственный слой.
setNeedsDisplay()
делает что-то, только если ваше представление реализует drawRect(_:)
.Не имеет значения, действительно ли функция что-то делает, но UIKit использует ее, чтобы определить, выполняете ли вы пользовательское рисование или нет.
Свойства, вероятно, больше не работают, потому что собственная реализация UIView
drawLayer(_:inContext:)
больше не вызывается.
Так что решение довольно простое.Просто вызовите реализацию суперкласса drawLayer(_:inContext:)
и реализуйте пустую drawRect(_:)
:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
super.drawLayer(layer, inContext: context)
let colorForCustomDrawing = layer.backgroundColor
// your custom drawing code
}
override func drawRect(rect: CGRect) {
// Although we use drawLayer(_:inContext:) we still need to implement this method.
// UIKit checks for its presence when it decides whether a call to setNeedsDisplay() is forwarded to its layer.
}
Сводка
Используйте drawRect(_:)
, пока у вас нетпроблема в том, что свойства возвращают неправильные значения во время анимации:
override func drawRect(rect: CGRect) {
// your custom drawing code
}
Используйте drawLayer(_:inContext:)
и drawRect(_:)
, если вам нужно получить доступ к текущему значению свойств вида / слоя, пока они находятсяанимированные:
override func drawLayer(layer: CALayer, inContext context: CGContext) {
super.drawLayer(layer, inContext: context)
let colorForCustomDrawing = layer.backgroundColor
// your custom drawing code
}
override func drawRect(rect: CGRect) {
// Although we use drawLayer(_:inContext:) we still need to implement this method.
// UIKit checks for its presence when it decides whether a call to setNeedsDisplay() is forwarded to its layer.
}