代码之家  ›  专栏  ›  技术社区  ›  Sweeper

如何使自定义视图的背景色属性可设置动画?

  •  1
  • Sweeper  · 技术社区  · 7 年前

    我想设置自定义视图背景色的动画。

    我的绘图代码非常简单。这个 draw(in:) 方法被重写如下:

    @IBDesignable
    class SquareView: UIView {
        override func draw(_ rect: CGRect) {
            super.draw(rect)
            let strokeWidth = self.width / 8
            let path = UIBezierPath()
            path.move(to: CGPoint(x: self.width - strokeWidth / 2, y: 0))
            path.addLine(to: CGPoint(x: self.width - strokeWidth / 2, y: self.height - strokeWidth / 2))
            path.addLine(to: CGPoint(x: 0, y: self.height - strokeWidth / 2))
            self.backgroundColor?.darker().setStroke()
            path.lineWidth = strokeWidth
            path.stroke()
        }
    }
    

    这个 darker 方法只返回调用它的颜色的较暗版本。

    当我将背景设置为蓝色时,它会绘制如下内容:

    enter image description here

    enter image description here

    我第一次尝试:

    UIView.animate(withDuration: 1) { 
        self.square.backgroundColor = .red
    }
    

    它只是在瞬间将颜色更改为红色,没有动画。

    CALayer s、 因此,我试着分层绘制:

    @IBDesignable
    class SquareViewLayers: UIView {
        dynamic var squareColor: UIColor = .blue {
            didSet {
                setNeedsDisplay()
            }
        }
    
        func setupView() {
            layer.backgroundColor = squareColor.cgColor
            let sublayer = CAShapeLayer()
            let path = UIBezierPath()
            let strokeWidth = self.width / 8
            path.move(to: CGPoint(x: self.width - strokeWidth / 2, y: 0))
            path.addLine(to: CGPoint(x: self.width - strokeWidth / 2, y: self.height - strokeWidth / 2))
            path.addLine(to: CGPoint(x: 0, y: self.height - strokeWidth / 2))
            self.tintColor.darker().setStroke()
            path.lineWidth = strokeWidth
            sublayer.path = path.cgPath
            sublayer.strokeColor = squareColor.darker().cgColor
            sublayer.lineWidth = strokeWidth
            sublayer.backgroundColor = UIColor.clear.cgColor
            layer.addSublayer(sublayer)
    
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupView()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            setupView()
        }
    }
    

    但结果很可怕

    enter image description here

    let anim = CABasicAnimation(keyPath: "squareColor")
    anim.fromValue = UIColor.blue
    anim.toValue = UIColor.red
    anim.duration = 1
    square.layer.add(anim, forKey: nil)
    

    但是什么也没发生。

    编辑:

    这个 深色 SwiftyColor . 它是如何实现的:

    // in an extension of UIColor
    public func darker(amount: CGFloat = 0.25) -> UIColor {
        return hueColor(withBrightnessAmount: 1 - amount)
    }
    
    private func hueColor(withBrightnessAmount amount: CGFloat) -> UIColor {
        var hue: CGFloat = 0
        var saturation: CGFloat = 0
        var brightness: CGFloat = 0
        var alpha: CGFloat = 0
    
        if getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) {
            return UIColor(hue: hue, saturation: saturation,
                               brightness: brightness * amount,
                               alpha: alpha)
        }
        return self
    }
    
    6 回复  |  直到 7 年前
        1
  •  2
  •   caseynolan    7 年前

    你在正确的轨道上 CABasicAnimation .

    1. 这个 keyPath: 在里面 let anim = CABasicAnimation(keyPath: "squareColor") backgroundColor .

    2. anim.fromValue anim.toValue CGColor 值(因为您在 CALayer ,它使用 CGColor公司 UIColor.blue.cgcolor 在这里

    不过,可以肯定的是,该动画不会为您保留颜色更改,因此您可能需要手动更改该属性。

        2
  •  1
  •   Luffy    7 年前

    这样你就不必再费心重画和颜色计算了,你可以使用基本的UIView。动画()


    ViewController。敏捷的

    import UIKit
    
    class ViewController: UIViewController {
        var squareView: SquareView!
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            squareView = SquareView.init(frame: CGRect(x:100, y:100, width:200, height:200))
            self.view.addSubview(squareView)
    
            squareView.backgroundColor = UIColor.blue
            UIView.animate(withDuration: 2.0, delay: 1.0, animations: {
                self.squareView.backgroundColor = UIColor.red
            }, completion:nil)
        }
    }
    

    SquareView。敏捷的

    import UIKit
    
    class SquareView: UIView {
        func setupView() {
            let shadow = LShadow.init(frame: self.bounds)
            self.addSubview(shadow)
        }
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupView()
        }
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            setupView()
        }
    }
    
    
    class LShadow: UIView {
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.backgroundColor = UIColor.clear
        }
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            self.backgroundColor = UIColor.clear
        }
        override func draw(_ rect: CGRect) {
            let strokeWidth = self.frame.size.width / 8
            let path = UIBezierPath()
            path.move(to: CGPoint(x: self.frame.size.width - strokeWidth / 2, y: 0))
            path.addLine(to: CGPoint(x: self.frame.size.width - strokeWidth / 2, y: self.frame.size.height - strokeWidth / 2))
            path.addLine(to: CGPoint(x: 0, y: self.frame.size.height - strokeWidth / 2))
            UIColor.init(white: 0, alpha: 0.5).setStroke()
            path.lineWidth = strokeWidth
            path.stroke()
        }
    }
    
        3
  •  1
  •   Ayman Ibrahim    7 年前

    CAKeyframeAnimation 你需要制作动画 backgroundColor 属性:

    class SquareView: UIView
    {
        let sublayer = CAShapeLayer()
    
        required init?(coder aDecoder: NSCoder)
        {
            super.init(coder: aDecoder)
            let frame = self.frame
            let strokeWidth = self.frame.width / 8
            sublayer.frame = self.bounds
    
            let path = UIBezierPath()
            path.move(to: CGPoint(x: frame.width  - strokeWidth / 2 , y: 0))
            path.addLine(to: CGPoint(x: frame.width - strokeWidth / 2, y: frame.height - strokeWidth / 2))
            path.addLine(to: CGPoint(x: 0, y: frame.height - strokeWidth / 2))
            path.addLine(to: CGPoint(x: 0 , y: 0))
            path.lineWidth = strokeWidth
            sublayer.path = path.cgPath
            sublayer.fillColor = UIColor.blue.cgColor
            sublayer.lineWidth = strokeWidth
            sublayer.backgroundColor = UIColor.blue.cgColor
            layer.addSublayer(sublayer)
        }
    
        //Action
        func animateIt()
        {
            let animateToColor = UIColor(cgColor: sublayer.backgroundColor!).darker().cgColor
            animateColorChange(mLayer: self.sublayer, colors: [sublayer.backgroundColor!, animateToColor], duration: 1)
        }
    
        func animateColorChange(mLayer:CALayer ,colors:[CGColor], duration:CFTimeInterval)
        {
            let animation = CAKeyframeAnimation(keyPath: "backgroundColor")
            CATransaction.begin()
            animation.keyTimes = [0.2, 0.5, 0.7, 1]
            animation.values = colors
            animation.calculationMode = kCAAnimationPaced
            animation.fillMode = kCAFillModeForwards
            animation.isRemovedOnCompletion = false
            animation.repeatCount = 0
            animation.duration = duration
            mLayer.add(animation, forKey: nil)
            CATransaction.commit()
        }
    }
    

    enter image description here

    注: 我已经编辑了答案,因此形状将始终适合从情节提要添加的父视图

        4
  •  1
  •   deoKasuhal    7 年前

    我有一个用动画改变视图背景颜色的实现,并在源代码中做了一些调整,使用CAShapeLayer添加了倒L形。我达到了要求的动画效果。这是视图的示例。

    class SquareView: UIView, CAAnimationDelegate {
    
        fileprivate func setup() {
            self.layer.addSublayer(self.sublayer)
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            self.setup
        }
    
        let sublayer = CAShapeLayer()
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            self.setup()
        }
    
        override func layoutSubviews() {
            super.layoutSubviews()
            let strokeWidth = self.bounds.width / 8
            var frame = self.bounds
            frame.size.width -= strokeWidth
            frame.size.height -= strokeWidth
            self.sublayer.path = UIBezierPath(rect: frame).cgPath
        }
    
    
    
        var color : UIColor = UIColor.clear {
            willSet {
                self.sublayer.fillColor = newValue.cgColor
                self.backgroundColor = newValue.darker()
            }
        }
    
    
    
        func animation(color: UIColor, duration: CFTimeInterval = 1.0) {
            let animation = CABasicAnimation()
            animation.keyPath = "backgroundColor"
            animation.fillMode = kCAFillModeForwards
            animation.duration = duration
            animation.repeatCount = 1
            animation.autoreverses = false
            animation.fromValue = self.backgroundColor?.cgColor
            animation.toValue = color.darker().cgColor
            animation.delegate = self;
    
            let shapeAnimation = CABasicAnimation()
            shapeAnimation.keyPath = "fillColor"
            shapeAnimation.fillMode = kCAFillModeForwards
            shapeAnimation.duration = duration
            shapeAnimation.repeatCount = 1
            shapeAnimation.autoreverses = false
            shapeAnimation.fromValue = self.sublayer.fillColor
            shapeAnimation.toValue = color.cgColor
            shapeAnimation.delegate = self;
    
    
            self.layer.add(animation, forKey: "kAnimation")
            self.sublayer.add(shapeAnimation, forKey: "kAnimation")
            self.color = color
        }
    
    
    
        public func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
            self.layer.removeAnimation(forKey: "kAnimation")
            self.sublayer.removeAnimation(forKey: "kAnimation")
        }
    }
    

    你可以用这个方法 animation(color: duration:) 在所需的动画持续时间内随动画更改颜色。

    实例

    let view = SquareView(frame: CGRect(x: 10, y: 100, width: 200, height: 300))
    view.color = UIColor.blue
    view.animation(color: UIColor.red) 
    

    结果如下

    enter image description here

        5
  •  1
  •   Sandeep    7 年前

    class SquareViewLayers: UIView, CAAnimationDelegate {
    
        var color: UIColor = UIColor.blue {
            didSet {
                animate(layer: backgroundLayer,
                        from: oldValue,
                        to: color)
                animate(layer: foregroundLayer,
                        from: oldValue.darker(),
                        to: color.darker())
            }
        }
    
        let backgroundLayer = CAShapeLayer()
        let foregroundLayer = CAShapeLayer()
    
        func setupView() {
    
            foregroundLayer.frame = frame
            layer.addSublayer(foregroundLayer)
    
            backgroundLayer.frame = frame
            layer.addSublayer(backgroundLayer)
    
    
            let strokeWidth = width / 8
    
            let backgroundPathRect = CGRect(origin: .zero, size: CGSize(width: width - strokeWidth,
                                                                        height: height - strokeWidth))
            let backgroundPath = UIBezierPath(rect: backgroundPathRect)
            backgroundLayer.fillColor = color.cgColor
            backgroundLayer.path = backgroundPath.cgPath
    
            let foregroundPath = UIBezierPath()
            foregroundPath.move(to: CGPoint(x: backgroundPathRect.width,
                                            y: 0))
            foregroundPath.addLine(to: CGPoint(x: width,
                                               y: 0))
            foregroundPath.addLine(to: CGPoint(x: width, y: height))
            foregroundPath.addLine(to: CGPoint(x: 0,
                                               y: height))
            foregroundPath.addLine(to: CGPoint(x: 0,
                                               y: backgroundPathRect.height))
            foregroundPath.addLine(to: CGPoint(x: backgroundPathRect.width,
                                               y: backgroundPathRect.height))
            foregroundPath.addLine(to: CGPoint(x: backgroundPathRect.width,
                                               y: 0))
            foregroundPath.close()
    
            foregroundLayer.path = foregroundPath.cgPath
            foregroundLayer.fillColor = color.darker().cgColor
        }
    
        override init(frame: CGRect) {
            super.init(frame: frame)
            setupView()
        }
    
        required init?(coder aDecoder: NSCoder) {
            super.init(coder: aDecoder)
            setupView()
        }
    
        func animate(layer: CAShapeLayer,
                     from: UIColor,
                     to: UIColor) {
            let fillColorAnimation = CABasicAnimation(keyPath: "fillColor")
            fillColorAnimation.fromValue = from.cgColor
            layer.fillColor = to.cgColor
            fillColorAnimation.duration = 1.0
            layer.add(fillColorAnimation,
                      forKey: nil)
        }
    }
    

    这是结果,

    enter image description here

        6
  •  1
  •   StaceyGirl    7 年前

    目标-C

    view.backgroundColor = [UIColor whiteColor].CGColor;
    
    [UIView animateWithDuration:2.0 animations:^{
        view.layer.backgroundColor = [UIColor greenColor].CGColor;
    } completion:NULL];
    

    敏捷的

    view.backgroundColor = UIColor.white.cgColor
    UIView.animate(withDuration: 2.0) { 
        view.backgroundColor = UIColor.green.cgColor
    }