2016-12-02 56 views
1

我通過繼承UIView,加入CAShapeLayer子層的層和壓倒一切的drawRect()更新的形狀層的path財產一個定製的圓形進度視圖。的CALayer與動畫路徑內容,請檢查的/設計性

通過使視圖@IBDesignableprogress屬性@IBInspectable,我能夠在Interface Builder中編輯它的值,並實時查看更新的貝塞爾路徑。非必要的,但非常酷!

接下來,我決定讓路徑變成動畫:無論您何時在代碼中設置一個新值,指示進度的弧線應該從零長度「增長」到達到圓的任何百分比(考慮活動中的弧線蘋果手錶應用程序)。

要做到這一點,我通過一個定製CALayer子類具有@dynamic@NSManaged)特性,觀察到與ananimation鍵換我CAShapeLayer子(我實現needsDisplayForKey()actionForKey()drawInContext()等)。

查看代碼(相關部分)看起來是這樣的:

// Triggers path update (animated) 
private var progress: CGFloat = 0.0 { 
    didSet { 
     updateArcLayer() 
    } 
} 

// Programmatic interface: 
// (pass false to achieve immediate change) 
func setValue(newValue: CGFloat, animated: Bool) { 
    if animated { 
     self.progress = newValue 
    } else { 
     arcLayer.animates = false 
     arcLayer.removeAllAnimations() 
     self.progress = newValue 
     arcLayer.animates = true 
    } 
} 

// Exposed to Interface Builder's inspector: 
@IBInspectable var currentValue: CGFloat { 
    set(newValue) { 
     setValue(newValue: currentValue, animated: false) 
     self.setNeedsLayout() 
    } 
    get { 
     return progress 
    } 
} 

private func updateArcLayer() { 
    arcLayer.frame = self.layer.bounds 
    arcLayer.progress = progress 
} 

而且代碼:

var animates: Bool = true 
@NSManaged var progress: CGFloat 

override class func needsDisplay(forKey key: String) -> Bool { 
    if key == "progress" { 
     return true 
    } 
    return super.needsDisplay(forKey: key) 
} 

override func action(forKey event: String) -> CAAction? { 
    if event == "progress" && animates == true { 
     return makeAnimation(forKey: event) 
    } 
    return super.action(forKey: event) 
} 

override func draw(in ctx: CGContext) { 
    ctx.beginPath() 

    // Define the arcs... 
    ctx.closePath() 
    ctx.setFillColor(fillColor.cgColor) 
    ctx.drawPath(using: CGPathDrawingMode.fill) 
} 

private func makeAnimation(forKey event: String) -> CABasicAnimation? { 
    let animation = CABasicAnimation(keyPath: event) 

    if let presentationLayer = self.presentation() { 
     animation.fromValue = presentationLayer.value(forKey: event) 
    } 

    animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) 
    animation.duration = animationDuration 
    return animation 
} 

的動畫作品,但現在我可以」讓我的路徑顯示在Interface Builder中。

我試圖實現我的觀點的prepareForInterfaceBuilder()這樣的:

override func prepareForInterfaceBuilder() { 
    super.prepareForInterfaceBuilder() 

    self.topLabel.text = "Hello, Interface Builder!" 
    updateArcLayer() 
} 

...和標籤文本的變化體現在界面生成器,但路徑不會被渲染。

我錯過了什麼嗎?

回答

1

那麼,是不是很可笑......原來我@Inspectable財產申報有很無聊錯誤。

你能發現它嗎?

@IBInspectable var currentValue: CGFloat { 
    set(newValue) { 
     setValue(newValue: currentValue, animated: false) 
     self.setNeedsLayout() 
    } 
    get { 
     return progress 
    } 
} 

它應該是:

@IBInspectable var currentValue: CGFloat { 
    set(newValue) { 
     setValue(newValue: newValue, animated: false) 
     self.setNeedsLayout() 
    } 
    get { 
     return progress 
    } 
} 

即,我被丟棄傳入的值(newValue),並使用當前的一個(currentValue)來設置內部變量,而不是。將值(始終爲0.0)記錄到Interface Builder(通過我的標籤的text屬性!)給了我一個線索。

現在它工作正常,無需drawRect()

+1

的drawRect()是核心動畫的存在的禍根。它應該與那些使用Core Graphics的人分離,並且在使用Core Animation的各個方面都受到影響。做得好! – Confused