Вдохновившись этим примером, я создали пользовательские CALayer
подклассы для клиньев и дуг. Они позволяют мне рисовать дуги и клинья и анимировать изменения в них, чтобы они проходили радиально.
Одно из разочарований в них заключается в том, что, по-видимому, когда вы идете по этому пути с подклассом drawInContext()
, вы ограничены клипом кадра слоя. Со стандартными слоями у вас есть masksToBounds
, который по умолчанию равен false
! Но кажется, что когда-то вы создали маршрут подкласса с рисованием, потому что неявно и неизменно true
.
Поэтому я решил попробовать другой подход, вместо этого создав подкласс CAShapeLayer
. И вместо добавления пользовательского drawInContext()
я бы просто изменил свои переменные для start
и sweep
на path
приемника. Это прекрасно работает, НО больше не будет анимироваться, как раньше:
import UIKit
class WedgeLayer:CAShapeLayer {
var start:Angle = 0.0.rotations { didSet { self.updatePath() }}
var sweep:Angle = 1.0.rotations { didSet { self.updatePath() }}
dynamic var startRadians:CGFloat { get {return self.start.radians.raw } set {self.start = newValue.radians}}
dynamic var sweepRadians:CGFloat { get {return self.sweep.radians.raw } set {self.sweep = newValue.radians}}
// more dynamic unit variants omitted
// we have these alternate interfaces because you must use a type
// which the animation framework can understand for interpolation purposes
override init(layer: AnyObject) {
super.init(layer: layer)
if let layer = layer as? WedgeLayer {
self.color = layer.color
self.start = layer.start
self.sweep = layer.sweep
}
}
override init() {
super.init()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func updatePath() {
let center = self.bounds.midMid
let radius = center.x.min(center.y)
print("updating path \(self.start) radius \(radius)")
if self.sweep.abs < 1.rotations {
let _path = UIBezierPath()
_path.moveToPoint(center)
_path.addArcWithCenter(center, radius: radius, startAngle: self.start.radians.raw, endAngle: (self.start + self.sweep).radians.raw, clockwise: self.sweep >= 0.radians ? true : false)
_path.closePath()
self.path = _path.CGPath
}
else {
self.path = UIBezierPath(ovalInRect: CGRect(around: center, width: radius * 2, height: radius * 2)).CGPath
}
}
override class func needsDisplayForKey(key: String) -> Bool {
return key == "startRadians" || key == "sweepRadians" || key == "startDegrees" || key == "sweepDegrees" || key == "startRotations" || key == "sweepRotations" || super.needsDisplayForKey(key)
}
}
Разве нельзя заставить его регенерировать путь и обновлять его, когда он анимирует значение? С оператором print()
я вижу, как он интерполирует значения, как и ожидалось, во время анимации. Я пытался добавить setNeedsDisplay()
в разные места, но безрезультатно.