首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >创建自定义动画属性

创建自定义动画属性
EN

Stack Overflow用户
提问于 2013-01-07 17:09:51
回答 3查看 14.4K关注 0票数 27

UIView上,可以将backgroundColour更改为动画。在UISlideView上,您可以更改动画的值。

您是否可以将自定义属性添加到您自己的UIView子类中,以便对其进行动画处理?

如果我在我的UIView中有一个CGPath,那么我可以通过改变绘制路径的百分比来对它的绘制进行动画处理。

我可以将动画封装到子类中吗。

例如,我有一个带CGPathUIView,它创建了一个圆。

如果圆圈不在那里,它代表0%。如果圆已满,则表示100%。我可以通过改变路径的绘制百分比来绘制任何值。我还可以通过对CGPath的百分比进行动画处理并重新绘制路径来实现(在UIView子类中)更改。

我可以在UIView上设置一些属性(即百分比),这样我就可以将更改粘贴到UIView animateWithDuration块中,并使路径百分比的更改具有动画效果吗?

我希望我已经解释了我想要做好的事情。

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

代码语言:javascript
复制
[UIView animateWithDuration:1.0
 animations:^{
     myCircleView.percentage = 0.7;
 }
 completion:nil];

并且圆路径动画到给定的百分比。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-01-07 17:59:02

如果您扩展了CALayer并实现了您定制

代码语言:javascript
复制
- (void) drawInContext:(CGContextRef) context

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

代码语言:javascript
复制
+ (BOOL) needsDisplayForKey:(NSString *) key {
    if ([key isEqualToString:@"percentage"]) {
        return YES;
    }
    return [super needsDisplayForKey:key];
}

当然,您还需要一个名为percentage@property。从现在开始,您可以使用核心动画对percentage属性进行动画处理。我没有使用[UIView animateWithDuration...]调用来检查它是否也可以工作。也许能行得通。但这对我很有效:

代码语言:javascript
复制
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"percentage"];
animation.duration = 1.0;
animation.fromValue = [NSNumber numberWithDouble:0];
animation.toValue = [NSNumber numberWithDouble:100];

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

哦,要在myCircleView中使用yourCustomLayer,请执行以下操作:

代码语言:javascript
复制
[myCircleView.layer addSublayer:myCustomLayer];
票数 26
EN

Stack Overflow用户

发布于 2013-01-07 17:50:57

你将不得不自己实现百分比部分。您可以覆盖图层绘制代码,根据设置的百分比值绘制您的cgpath。查看core animation programming guideanimation types and timing guide

票数 0
EN

Stack Overflow用户

发布于 2020-06-01 22:41:23

@David Rees answer让我走上了正确的道路,但有一个问题。在我的例子中,动画的完成总是在动画开始之后返回false。

代码语言:javascript
复制
UIView.animate(withDuration: 2, delay: 0, options: .curveEaseInOut, animations: {
    circularProgress.progress = 0.76
}, completion: { finished in
   // finished - always false
})

这就是它为我工作的方式-动画的动作是在CALayer内部处理的。

我还包括了一个小例子,如何使层与其他属性,如“颜色”。在这种情况下,如果没有复制值的初始化器,则更改颜色仅对非动画视图有效。在动画期间,它将是可见的“默认设置”。

代码语言:javascript
复制
public class CircularProgressView: UIView {

    @objc public dynamic var progress: CGFloat {
        get {
            return progressLayer.progress
        }
        set {
            progressLayer.progress = newValue
        }
    }

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

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


/*
* 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


    var color: UIColor = .red
    // preserve layer properties
    // without this specyfic init, if color was changed to sth else
    // animation would still use .red
    override init(layer: Any) {
        super.init(layer: layer)
        if let layer = layer as? CircularProgressLayer {
            self.color = layer.color
            self.progress = layer.progress
        }
    }

    override init() {
        super.init()
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

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

    override func action(forKey event: String) -> CAAction? {
        if event == #keyPath(CircularProgressLayer.progress) {
            guard let animation = action(forKey: #keyPath(backgroundColor)) as? CABasicAnimation else {
                setNeedsDisplay()
                return nil
            }

            if let presentation = presentation() {
                animation.keyPath = event
                animation.fromValue = presentation.value(forKeyPath: event)
                animation.toValue = nil
            } else {
                return nil
            }

            return animation
        }

        return super.action(forKey: event)
    }

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

        UIGraphicsPushContext(ctx)

        //Light Gray
        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 - default
        self.color.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()
    }
}

我在本文中找到的以不同方式处理动画和复制层属性的方法:https://medium.com/better-programming/make-apis-like-apple-animatable-view-properties-in-swift-4349b2244cea

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14192816

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档