【问题标题】:How to animate a fill color change on UIBezierPath?如何为 UIBezierPath 上的填充颜色变化设置动画?
【发布时间】:2016-04-10 20:06:27
【问题描述】:

开始时形状已正确填充,但我不知道如何更改填充颜色,或者更好的是,如何在UIBezierPath 上设置填充颜色更改的动画。就像我正在寻找改变 UIView 的背景颜色一样。

var fillColor = UIColor()

func changeBackgroundColor() {

    let animcolor = CABasicAnimation(keyPath: "fillColor")
    animcolor.fromValue = UIColor.greenColor()
    animcolor.toValue = UIColor.orangeColor()
    animcolor.duration = 1.0;
    animcolor.repeatCount = 0;
    animcolor.autoreverses = true
    shapeLayer.addAnimation(animcolor, forKey: "fillColor")

}


var fillColor = UIColor()
let clipPath = UIBezierPath()
let shapeLayer = CAShapeLayer()

override func drawRect(rect: CGRect) {

        clipPath.moveToPoint(CGPointMake(self.bounds.minX + 7.65, self.bounds.minY - 0.25))
        clipPath.addCurveToPoint(CGPointMake(self.bounds.minX + 7.65, self.bounds.minY + 36.1), controlPoint1: CGPointMake(self.bounds.minX - 2.38, self.bounds.minY + 9.79), controlPoint2: CGPointMake(self.bounds.minX - 2.38, self.bounds.minY + 26.06))
        clipPath.addCurveToPoint(CGPointMake(self.bounds.minX + 43.99, self.bounds.minY + 36.1), controlPoint1: CGPointMake(self.bounds.minX + 17.69, self.bounds.minY + 46.13), controlPoint2: CGPointMake(self.bounds.minX + 33.96, self.bounds.minY + 46.13))
        clipPath.addLineToPoint(CGPointMake(self.bounds.minX + 43.99, self.bounds.minY + 36.1))
        clipPath.addLineToPoint(CGPointMake(self.bounds.minX + 44.01, self.bounds.minY + 0.19))
        clipPath.addLineToPoint(CGPointMake(self.bounds.minX + 7.58, self.bounds.minY + 0.19))
        clipPath.usesEvenOddFillRule = true
        fillColor = userColor
    }

clipPath.addClip()
fillColor.setFill()
clipPath.fill()
shapeLayer.path = clipPath.CGPath
self.layer.mask = shapeLayer

【问题讨论】:

    标签: ios swift


    【解决方案1】:

    就像我要改变 UIView 的背景颜色一样。

    但是为什么你不能完全做到这一点?您已经将视图蒙版到您的路径 - 因此为您的背景颜色设置动画将达到您想要的确切效果。只需摆脱 Core Graphics 路径填充 - 并像 can't use drawRect with an animation of the background 一样删除 drawRect 覆盖。

    如果您真的需要使用drawRect 并为颜色设置动画,您可以考虑将UIView 拆分为两个UIViews - 一个用于绘制背景层(您可以对其进行动画处理) ),以及一个绘制自定义绘图。或者,您可以设置CADisplayLink 以使用中间背景颜色重新绘制每一帧的视图——但这在性能方面并不令人惊奇。

    正如我在下面所说,您不能直接为UIBezierPath 的填充颜色设置动画。如果您只想使用现有代码更改(而不是动画)填充颜色,那么您只需使用不同的 userColor 调用 setNeedsDisplay

    如果您之所以问这个问题是因为您使用两种不同的路径进行遮罩和填充,请参阅我下面的答案(事后看来过于复杂)。


    问题是UIBezierPath 的填充颜色不是可动画的。但是CAShapeLayer 上的填充颜色是。

    因此,您想使用另一个CAShapeLayer 来代替填充。我建议您创建一个fillLayer 和一个maskLayer 属性,以明确它们的功能。现在因为您使用的是 100% 分层的方法,您可以将代码移出 drawRect,因为您不再进行任何核心图形绘图。

    另外,值得注意的是,您需要使用CGColor 来实现值的动画。

    这样的事情应该可以解决问题:

    var fillColor = UIColor.greenColor()
    let maskLayer = CAShapeLayer()
    let fillLayer = CAShapeLayer()
    
    func changeBackgroundColor() {
    
        let animcolor = CABasicAnimation(keyPath: "fillColor")
        animcolor.fromValue = UIColor.greenColor().CGColor
        animcolor.toValue = UIColor.orangeColor().CGColor
        animcolor.duration = 1.0;
        animcolor.repeatCount = 0;
        animcolor.autoreverses = true
        fillLayer.addAnimation(animcolor, forKey: "fillColor")
    
    }
    
    // re-adjust the clipping path when the view bounds changes
    override func layoutSubviews() {
    
        let clipPath = UIBezierPath()
        clipPath.moveToPoint(CGPointMake(self.bounds.minX + 7.65, self.bounds.minY - 0.25))
        clipPath.addCurveToPoint(CGPointMake(self.bounds.minX + 7.65, self.bounds.minY + 36.1), controlPoint1: CGPointMake(self.bounds.minX - 2.38, self.bounds.minY + 9.79), controlPoint2: CGPointMake(self.bounds.minX - 2.38, self.bounds.minY + 26.06))
        clipPath.addCurveToPoint(CGPointMake(self.bounds.minX + 43.99, self.bounds.minY + 36.1), controlPoint1: CGPointMake(self.bounds.minX + 17.69, self.bounds.minY + 46.13), controlPoint2: CGPointMake(self.bounds.minX + 33.96, self.bounds.minY + 46.13))
        clipPath.addLineToPoint(CGPointMake(self.bounds.minX + 43.99, self.bounds.minY + 36.1))
        clipPath.addLineToPoint(CGPointMake(self.bounds.minX + 44.01, self.bounds.minY + 0.19))
        clipPath.addLineToPoint(CGPointMake(self.bounds.minX + 7.58, self.bounds.minY + 0.19))
    
        // update layer paths
        fillLayer.path = clipPath.CGPath
        maskLayer.path = clipPath.CGPath
    
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
    
        // configure filling
        fillLayer.fillColor = fillColor.CGColor
        fillLayer.fillRule = kCAFillRuleEvenOdd
    
        // add fill path to superlayer
        layer.addSublayer(fillLayer)
    
        // configure masking layer
        maskLayer.fillRule = kCAFillRuleEvenOdd
        layer.mask = maskLayer
    }
    

    【讨论】:

    • 太棒了。感谢您将其转化为更好的实施。但是我无法将fillLayer 的填充颜色设置为动画,我什至无法更改它。该函数肯定被调用,我正在记录它以确保。这就是我坐下来想我会在 30 到 40 分钟内完成的事情,结果却需要半天时间。
    • @spacemonkey fillLayer 是否在动画之前显示在屏幕上?确保它的 frame 不为零,并且它已添加到层层次结构中。如果您使用我上面的代码,您需要使用 init(frame:) 初始化程序初始化 UIView(实际上您希望将任何与大小相关的代码移动到更合适的位置,例如 layoutSubviews )。
    • 哦,它就在那里。确切的位置和大小,它应该是。填充颜​​色也设置为应有的颜色,只是不会更改为其他颜色。
    • 嗯……很奇怪。上面的代码在测试项目中对我来说工作正常。您是否确保将 .CGColor 添加到动画的 to 和 from 值?
    • 我知道,这太疯狂了。甚至注释掉了整个班级并逐字输入。
    【解决方案2】:

    Swift 4 版本,你可以在 Playground 中查看代码

    var fillColor = UIColor.green
    let maskLayer = CAShapeLayer()
    let fillLayer = CAShapeLayer()
    
    func changeBackgroundColor() {
        
        let animcolor = CABasicAnimation(keyPath: "fillColor")
        animcolor.fromValue = UIColor.green.cgColor
        animcolor.toValue = UIColor.orange.cgColor
        animcolor.duration = 1.0;
        animcolor.repeatCount = 0;
        animcolor.autoreverses = true
        fillLayer.add(animcolor, forKey: "fillColor")
        
    }
    
    // re-adjust the clipping path when the view bounds changes
    
    let view = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
    view.backgroundColor = UIColor.white
    
    func createPaths() {
        let clipPath = UIBezierPath()
        clipPath.move(to: CGPoint(x: view.bounds.minX + 7.65, y: view.bounds.minY - 0.25))
        clipPath.addCurve(to: CGPoint(x: view.bounds.minX + 7.65, y: view.bounds.minY + 36.1),
                          controlPoint1: CGPoint(x: view.bounds.minX - 2.38, y: view.bounds.minY + 9.79),
                          controlPoint2: CGPoint(x: view.bounds.minX - 2.38, y: view.bounds.minY + 26.06))
        clipPath.addCurve(to: CGPoint(x: view.bounds.minX + 43.99, y: view.bounds.minY + 36.1),
                          controlPoint1: CGPoint(x: view.bounds.minX + 17.69, y: view.bounds.minY + 46.13),
                          controlPoint2: CGPoint(x: view.bounds.minX + 33.96, y: view.bounds.minY + 46.13))
        
        clipPath.addLine(to: CGPoint(x: view.bounds.minX + 43.99, y: view.bounds.minY + 36.1))
        clipPath.addLine(to: CGPoint(x: view.bounds.minX + 44.01, y: view.bounds.minY + 0.19))
        clipPath.addLine(to: CGPoint(x: view.bounds.minX + 7.58, y: view.bounds.minY + 0.19))
        
        // update layer paths
        fillLayer.path = clipPath.cgPath
        maskLayer.path = clipPath.cgPath
        
        fillLayer.fillColor = fillColor.cgColor
        fillLayer.fillRule = .evenOdd
        view.layer.addSublayer(fillLayer)
        maskLayer.fillRule = .evenOdd
        view.layer.mask = maskLayer
    }
    
    createPaths()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-24
      相关资源
      最近更新 更多