问题
I have a custom CALayer
subclass that draws a circular arc. It looks something like:
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)
}
}
I'm using a custom subclass, because while the path
of a CAShapeLayer
is animatable, it does not animate in the way you would expect the arc to rotate around. The above works. Animations to the start/sweep produce the desired effect.
I want to change it though. I have an Angle
struct (see this answer) which I would rather use than the raw radian CGFloat value. Changing the type of those two vars to Angle
compiles fine. And the original display works too (after making the appropriate conversions/extractions from the Angle
vars). THE PROBLEM is being able to animate those vars now. Where as before I might have written some code like:
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")
I can't just change those to Angle
values:
sweep.fromValue = Angle(raw: 0, unit: .Rotations)
sweep.toValue = Angle(raw: 0, unit: .Rotations)
That results in a
cannot assign value of type 'Angle' to type of 'AnyObject?'
I'm hoping that the solution is that I need to somehow convert/wrap my Angle
values as NSValue objects? Is that indeed correct? Can it be done? How so?
I really like Swift, but the corner cases where it intersects with Objc can be really confusing.
回答1:
Angle
is a struct, which does not conform to AnyObject
. In order to conform to AnyObject
, you need to change it to a class, or wrap it into a class like Box
:
final class Box<T> {
let value: T
init(_ value: T) { self.value = value }
}
That said, this is unlikely to work. CABasicAnimation
has special knowledge about how to interpolate specific things:
- integers and doubles
- CGRect, CGPoint, CGSize, and CGAffineTransform structures
- CATransform3D data structures
- CGColor and CGImage references
I don't believe there's any way to add to this list. I don't believe you can use CABasicAnimation
to interpolate arbitrary types. You need to animate from a number to a number, so that's going to be basically the code you have above.
Of course you can make sweepRadians
be a computed property that just forwards to sweep
, and you could animate from Degrees(0).rawRadians
to Degrees(360).rawRadians
. So you can get most of the practical benefits you want; just not without manually converting to a number at some point.
Now if this is Mac, you can subclass NSAnimation
and build your own animator that can do this. See CABasicAnimation and custom types for discussion of that. But there's no NSAnimation
on iOS, and I don't believe you can subclass CAAnimation
in the same way. There's no "progress" delegate method on CAAnimation
.
回答2:
I think you should try using init(pointer pointer: UnsafePointer<Void>)
of NSValue
. You may obtain the pointer to your Angle
instance using func withUnsafePointer<T, Result>(inout arg: T, @noescape _ body: UnsafePointer<T> throws -> Result) rethrows -> Result
(see http://swiftdoc.org/v2.0/func/withUnsafePointer/ for reference).
P.S. The idea of passing NSValue-wrapped things to Core Animation is a working technique in general. It's still unclear if CA will unwrap Angle though. Good luck.
来源:https://stackoverflow.com/questions/32893429/convert-wrap-swift-struct-as-nsvalue-for-caanimation-purposes