Преобразовать / обернуть структуру Swift как NSValue для целей CAAnimation?
У меня есть обычай CALayer
подкласс, который рисует дугу окружности. Это выглядит примерно так:
class ArcLayer: CALayer {
var strokeColor:UIColor = UIColor.blackColor() { didSet { self.setNeedsDisplay() }}
var strokeWidth:CGFloat = 1.0 { didSet { self.setNeedsDisplay() }}
var strokeCap:CGLineCap = .Butt { didSet { self.setNeedsDisplay() }}
var startRadians:CGFloat = 0.0 { didSet { self.setNeedsDisplay() }}
var sweepRadians:CGFloat = Tau { didSet { self.setNeedsDisplay() }}
// other init's omitted for brevity
override init(layer: AnyObject) {
super.init(layer: layer)
if let layer = layer as? ArcLayer {
self.strokeColor = layer.strokeColor
self.strokeWidth = layer.strokeWidth
self.strokeCap = layer.strokeCap
self.startRadians = layer.startRadians
self.sweepRadians = layer.sweepRadians
}
}
override func drawInContext(ctx: CGContext) {
...
}
override class func needsDisplayForKey(key: String) -> Bool {
return key == "startRadians" || key == "sweepRadians" || super.needsDisplayForKey(key)
}
}
Я использую пользовательский подкласс, потому что пока path
из CAShapeLayer
анимация, она не анимируется так, как вы ожидаете, что дуга будет вращаться вокруг. Выше работает. Анимации для запуска / развертки дают желаемый эффект.
Я хочу изменить это все же. у меня есть Angle
struct (см. этот ответ), которую я предпочел бы использовать, а не необработанное значение CGFloat в радианах. Меняя тип этих двух переменных на Angle
компилируется нормально. И оригинальный дисплей тоже работает (после соответствующих преобразований / извлечений из Angle
вары). ПРОБЛЕМА сейчас в состоянии оживить эти переменные. Где, как и прежде, я мог бы написать такой код:
let sweep = CABasicAnimation(keyPath: "sweepRadians")
sweep.fromValue = 0.0 // raw radians value
sweep.toValue = Tau // raw radians value for one rotation
sweep.duration = 10.seconds
sweep.repeatCount = 0
self.blueRing.addAnimation(sweep, forKey: "sweepRadians")
Я не могу просто изменить их на Angle
ценности:
sweep.fromValue = Angle(raw: 0, unit: .Rotations)
sweep.toValue = Angle(raw: 0, unit: .Rotations)
Это приводит к
cannot assign value of type 'Angle' to type of 'AnyObject?'
Я надеюсь, что решение состоит в том, что мне нужно как-то преобразовать / обернуть Angle
значения как объекты NSValue? Это действительно правильно? Это можно сделать? Как так?
Мне очень нравится Swift, но угловые случаи, когда он пересекается с Objc, могут быть очень запутанными.
2 ответа
Angle
это структура, которая не соответствует AnyObject
, Для того, чтобы соответствовать AnyObject
, вам нужно изменить его на класс, или обернуть его в класс, как Box
:
final class Box<T> {
let value: T
init(_ value: T) { self.value = value }
}
Тем не менее, это вряд ли сработает. CABasicAnimation
имеет специальные знания о том, как интерполировать конкретные вещи:
- целые и двойные числа
- Структуры CGRect, CGPoint, CGSize и CGAffineTransform
- CATransform3D структуры данных
- CGColor и CGImage ссылки
Я не верю, что есть какой-то способ добавить в этот список. Я не верю, что вы можете использовать CABasicAnimation
интерполировать произвольные типы. Вы должны анимировать от числа к числу, так что это будет в основном код, который вы имеете выше.
Конечно можно сделать sweepRadians
быть вычисляемым свойством, которое просто пересылает sweep
и вы могли бы оживить из Degrees(0).rawRadians
в Degrees(360).rawRadians
, Таким образом, вы можете получить большинство практических преимуществ, которые вы хотите; просто не без ручного преобразования в число в какой-то момент.
Теперь, если это Mac, вы можете подкласс NSAnimation
и создать свой собственный аниматор, который может сделать это. См. CABasicAnimation и пользовательские типы для обсуждения этого. Но нет NSAnimation
на iOS, и я не верю, что вы можете подкласс CAAnimation
таким же образом. Там нет "прогресс" метод делегата на CAAnimation
,
Я думаю, что вы должны попробовать использовать init(pointer pointer: UnsafePointer<Void>)
из NSValue
, Вы можете получить указатель на ваш Angle
пример использования func withUnsafePointer<T, Result>(inout arg: T, @noescape _ body: UnsafePointer<T> throws -> Result) rethrows -> Result
(см. http://swiftdoc.org/v2.0/func/withUnsafePointer/ для справки).
PS Идея передачи NSValue-обернутых вещей в Core Animation является рабочей техникой в целом. Пока неясно, развернет ли CA Angle. Удачи.