Я сделал заказ кругового обзора прогресса на подклассы UIView
, добавив CAShapeLayer
подслой его слой и наиважнейшей drawRect()
обновить path
свойства формы слоя.CALayer с Animated Path содержанием, осматриваемыми/DESIGNABLE
Сделав вид @IBDesignable
и progress
@IBInspectable
, я смог изменить его значение в Interface Builder и увидеть обновленный путь безье в реальном времени. Не важно, но действительно круто!
Далее я решил сделать анимированный путь: всякий раз, когда вы устанавливаете новое значение в коде, дуга, указывающая прогресс, должна «расти» от нулевой длины до любого процента круга (подумайте о дугах в Деятельности приложение в Apple Watch).
Для достижения этой цели, я сменил свою CAShapeLayer
подслоя с помощью настраиваемой CALayer
подкласса, который имеет свойство @dynamic
(@NSManaged
), наблюдаемый в качестве ключа для ananimation (я реализовал 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
}
анимационной работы, но теперь я могу» t получить мои пути для отображения в Interface Builder.
Я попытался реализации мой взгляд-х prepareForInterfaceBuilder()
так:
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
self.topLabel.text = "Hello, Interface Builder!"
updateArcLayer()
}
... и изменение текста этикетки отражается в Interface Builder, но путь не отображается.
Я что-то упустил?
DrawRect() не является проклятием существования Core Animation в. Он должен быть изолирован от тех, кто использует Core Graphics и поражен из всех аспектов использования Core Animation. Отлично сработано! – Confused