Преобразовать / обернуть структуру 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. Удачи.

Другие вопросы по тегам