创建自定义的可动画属性

Fog*_*ter 23 iphone core-animation uiview ios

UIView你可以更改backgroundColour动画.在a上UISlideView你可以改变动画的值.

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

如果我CGPath在我的内部,UIView那么我可以通过改变路径绘制的百分比来设置它的绘图.

我可以将该动画封装到子类中.

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

如果圆圈不存在则表示0%.如果圆圈已满,则表示100%.我可以通过改变路径绘制的百分比来绘制任何值.我还可以通过设置动画的百分比CGPath并重新绘制路径来为变化(在UIView子类中)设置动画.

我可以设置一些属性(即百分比),UIView以便我可以将更改粘贴到一个UIView animateWithDuration块中,它可以设置路径百分比变化的动画吗?

我希望我已经解释了我想做得好的事情.

基本上,我想做的就是...

[UIView animateWithDuration:1.0
 animations:^{
     myCircleView.percentage = 0.7;
 }
 completion:nil];
Run Code Online (Sandbox Code Playgroud)

并且圆形路径以给定百分比为动画.

Tom*_*ren 23

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

- (void) drawInContext:(CGContextRef) context
Run Code Online (Sandbox Code Playgroud)

您可以通过覆盖needsDisplayForKey(在您的自定义CALayer类中)来制作可动画的属性,如下所示:

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

当然,你还需要一个@property被叫percentage.从现在开始,您可以使用核心动画为百分比属性设置动画.我也没有检查它是否也可以使用该[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"];
Run Code Online (Sandbox Code Playgroud)

哦,使用yourCustomLayermyCircleView,这样做:

[myCircleView.layer addSublayer:myCustomLayer];
Run Code Online (Sandbox Code Playgroud)


Dav*_*ees 14

完成Swift 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 = (action.copy() as? CABasicAnimation) {
            animation.keyPath = #keyPath(CircularProgressLayer.progress)
            animation.fromValue = progressLayer.progress
            animation.toValue = progress
            self.layer.add(animation, forKey: #keyPath(CircularProgressLayer.progress))
            return animation
        }
        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)
Run Code Online (Sandbox Code Playgroud)

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

以及在这里使用相同概念的objc项目:

当从动画块动画对象时,将基本上调用action(for layer :),我们可以使用相同的属性(从backgroundColor属性中窃取)启动我们自己的动画,并为更改设置动画.