创建自定义动画属性(Create a custom animatable property)

2019-07-17 22:18发布

UIView ,你可以改变动画的backgroundColour。 并在UISlideView您可以更改动画值。

您可以将自定义属性添加到您自己的UIView子类,所以它可以是动画?

如果我有一个CGPath我内UIView那么我可以通过改变绘制的路径的百分比动画它的绘制。

我可以封装该动画到子类。

即我有一个UIView一个CGPath创建一个圆。

如果圆圈是不是有它代表0%。 如果圈是充满它代表了100%。 我可以通过改变绘制的路径的百分比得出任何价值。 我还可以通过动画的百分比动画(UIView的子类中)的变化CGPath和重绘路径。

我可以在设置一些属性(即百分比) UIView ,使我能够坚持改变成UIView animateWithDuration块,并将其动画路径的百分比的变化?

我希望我已经解释过,我想做好什么。

从本质上讲,所有我想要做的是一样的东西...

[UIView animateWithDuration:1.0
 animations:^{
     myCircleView.percentage = 0.7;
 }
 completion:nil];

和圆路径动画给定的百分比。

Answer 1:

如果扩展的CALayer并实现自定义

- (void) drawInContext:(CGContextRef) context

您可以通过覆盖让一个动画属性needsDisplayForKey (在自定义的CALayer类)就像这样:

+ (BOOL) needsDisplayForKey:(NSString *) key {
    if ([key isEqualToString:@"percentage"]) {
        return YES;
    }
    return [super needsDisplayForKey:key];
}

当然,你还需要有一个@propertypercentage 。 从现在起,你可以使用核心动画动画的百分比属性。 我没有检查它是否有效使用[UIView animateWithDuration...]打电话为好。 它可能会奏效。 但这个工作对我来说:

CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"percentage"];
animation.duration = 1.0;
animation.fromValue = [NSNumber numberWithDouble:0];
animation.toValue = [NSNumber numberWithDouble:100];

[myCustomLayer addAnimation:animation forKey:@"animatePercentage"];

哦,使用yourCustomLayermyCircleView ,这样做:

[myCircleView.layer addSublayer:myCustomLayer];


Answer 2:

完成斯威夫特3例子:

public class CircularProgressView: UIView {

    public dynamic var progress: CGFloat = 0 {
        didSet {
            progressLayer.progress = progress
        }
    }

    fileprivate var progressLayer: CircularProgressLayer {
        return layer as! CircularProgressLayer
    }

    override public class var layerClass: AnyClass {
        return CircularProgressLayer.self
    }


    override public func action(for layer: CALayer, forKey event: String) -> CAAction? {
        if event == #keyPath(CircularProgressLayer.progress),
            let action = action(for: layer, forKey: #keyPath(backgroundColor)) as? CAAnimation {

            let animation = CABasicAnimation()
            animation.keyPath = #keyPath(CircularProgressLayer.progress)
            animation.fromValue = progressLayer.progress
            animation.toValue = progress
            animation.beginTime = action.beginTime
            animation.duration = action.duration
            animation.speed = action.speed
            animation.timeOffset = action.timeOffset
            animation.repeatCount = action.repeatCount
            animation.repeatDuration = action.repeatDuration
            animation.autoreverses = action.autoreverses
            animation.fillMode = action.fillMode
            animation.timingFunction = action.timingFunction
            animation.delegate = action.delegate
            self.layer.add(animation, forKey: #keyPath(CircularProgressLayer.progress))
        }
        return super.action(for: layer, forKey: event)
    }

}



/*
* Concepts taken from:
* https://stackoverflow.com/a/37470079
*/
fileprivate class CircularProgressLayer: CALayer {
    @NSManaged var progress: CGFloat
    let startAngle: CGFloat = 1.5 * .pi
    let twoPi: CGFloat = 2 * .pi
    let halfPi: CGFloat = .pi / 2


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

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

        UIGraphicsPushContext(ctx)

        //Light Grey
        UIColor.lightGray.setStroke()

        let center = CGPoint(x: bounds.midX, y: bounds.midY)
        let strokeWidth: CGFloat = 4
        let radius = (bounds.size.width / 2) - strokeWidth
        let path = UIBezierPath(arcCenter: center, radius: radius, startAngle: 0, endAngle: twoPi, clockwise: true)
        path.lineWidth = strokeWidth
        path.stroke()


        //Red
        UIColor.red.setStroke()

        let endAngle = (twoPi * progress) - halfPi
        let pathProgress = UIBezierPath(arcCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle , clockwise: true)
        pathProgress.lineWidth = strokeWidth
        pathProgress.lineCapStyle = .round
        pathProgress.stroke()

        UIGraphicsPopContext()
    }
}

let circularProgress = CircularProgressView(frame: CGRect(x: 0, y: 0, width: 80, height: 80))
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut, animations: {
    circularProgress.progress = 0.76
}, completion: nil)

有一个伟大的objc文章在这里 ,它进入细节关于如何工作

除了使用相同概念的objc项目在这里 :

从本质上讲动作(层当一个对象被从一个动画块动画:)将被调用,我们就可以开始我们自己的具有相同属性(由backgroundColor属性被盗动画)和动画的变化。



Answer 3:

对于谁需要对更多的细节像我一样的那些:

有一个很酷的苹果例子覆盖这个问题。

例如,多亏了它,我发现你实际上并不需要您的自定义层添加为子层(如@汤姆面包车Zummeren建议)。 相反,它足以类方法添加到您的视图类:

+ (Class)layerClass
{
    return [CustomLayer class];
}

希望它可以帮助别人。



Answer 4:

你将不得不自己实现的百分比计算的部分。 您可以覆盖层绘图代码来绘制cgpath accroding设定的百分比值。 检出芯动画节目指南和动画类型和同步导



文章来源: Create a custom animatable property