2015-10-06 81 views
3

this example的啓發,我爲楔子和弧創建了自定義的CALayer子類。允許我繪製弧形和楔形,並在其中生成動畫變化,以便它們沿徑向掃動。正確的方法來劃分CAShapeLayer

與他們的挫折之一是,顯然當你走這個路線有一個子類drawInContext()你受限於層的框架的剪輯。使用庫存圖層,您有默認falsemasksToBounds!但它似乎一旦你的子類路由與繪圖,那是因爲隱含和不可改變的true

所以我想我會嘗試一種不同的方法,而不是繼承CAShapeLayer。而不是增加一個自定義drawInContext(),我只需要我的變量startsweep更新接收器的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(),但無濟於事。

回答

1

有幾個技巧,以得到這正常工作:

  • 不要使用didSet。相反,您應該覆蓋display()並在那裏更新self.path。使用(presentation() as! WedgeLayer? ?? self).sweep訪問該屬性。 (presentation,被稱爲的OBJ-C和斯威夫特2 presentationLayer,讓您在動畫期間訪問當前可見的屬性值。)

  • 如果你想暗示動畫,在this answer描述的實施actionForKey

+0

真棒和succint的答案!前兩發子彈是否有效?我可以請求你補充說明爲什麼這種方法有效,而我的沒有?特別是第二點要做的是什麼。它顯然是克服了第一個原因。當我第一次做*時,我通過我的print()聲明注意到,當它通過動畫時(這是我用原始方法看到的迴歸),值沒有改變。 –