Swift - 彩色填充动画

时间:2016-06-28 12:26:59

标签: ios swift uitableview core-animation

我对ios动画相对较新,我相信我为UIView制作动画的方法有问题。

我将从UI截图开始,更精确地描绘我的问题:

有一个tableView单元格,带有两个标签和彩色圆圈 enter image description here

每当我向单元格引入新值时,我想为这个最左边的灯泡设置动画,让它看起来像是充满了红色。

这是BadgeView的实现,它基本上是前面提到的最左边的圆圈

class BadgeView: UIView {

var coeff:CGFloat = 0.5

override func drawRect(rect: CGRect) {
    let topRect:CGRect = CGRectMake(0, 0, rect.size.width, rect.size.height*(1.0 - self.coeff))
    UIColor(red: 249.0/255.0, green: 163.0/255.0, blue: 123.0/255.0, alpha: 1.0).setFill()
    UIRectFill(topRect)

    let bottomRect:CGRect = CGRectMake(0, rect.size.height*(1-coeff), rect.size.width, rect.size.height*coeff)
    UIColor(red: 252.0/255.0, green: 95.0/255.0, blue: 95.0/255.0, alpha: 1.0).setFill()
    UIRectFill(bottomRect)
            self.layer.cornerRadius = self.frame.height/2.0
    self.layer.masksToBounds = true
   }
}

这是我实现不均匀填充的方式 - 我引入了我在viewController中修改的系数。

在我的cellForRowAtIndexPath方法中,我尝试使用带回调的自定义按钮设置此形状的动画

let btn:MGSwipeButton = MGSwipeButton(title: "", icon: img, backgroundColor: nil, insets: ins, callback: {
        (sender: MGSwipeTableCell!) -> Bool in
        print("Convenience callback for swipe buttons!")
        UIView.animateWithDuration(4.0, animations:{ () -> Void in
            cell.pageBadgeView.coeff = 1.0
            let frame:CGRect = cell.pageBadgeView.frame
            cell.pageBadgeView.drawRect(frame)
        })
        return true
        }) 

但它只会打印到控制台

  

:CGContextSetFillColorWithColor:无效的上下文0x0。如果你想查看回溯,请设置CG_CONTEXT_SHOW_BACKTRACE环境变量。

虽然我很想知道正确的答案和方法,但是出于教育目的,知道这段代码不起作用会很棒。 提前致谢

3 个答案:

答案 0 :(得分:9)

问题的错误部分似乎是代码的这一部分:

cell.pageBadgeView.drawRect(frame)

来自UIView drawRect上的Apple docs

  

首次显示视图或发生使视图的可见部分无效的事件时,将调用此方法。你永远不应该直接调用这个方法。要使视图的一部分无效,从而导致重绘该部分,请调用setNeedsDisplay或setNeedsDisplayInRect:方法。

因此,如果您将代码更改为:

cell.pageBadgeView.setNeedsDisplay() 

您将摆脱错误并正确查看badgeView 已填充。但是这不会动画,因为默认情况下drawRect不可动画。

对于您的问题,最简单的解决方法是让BadgeView拥有填充颜色的内部视图。我将重构BadgeView:

class BadgeView: UIView {

    private let fillView = UIView(frame: CGRectZero)

    private var coeff:CGFloat = 0.5 {
        didSet {
            // Make sure the fillView frame is updated everytime the coeff changes
            updateFillViewFrame()
        }
    }

    // Only needed if view isn't created in xib or storyboard
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    // Only needed if view isn't created in xib or storyboard
    override init(frame: CGRect) {
        super.init(frame: frame)

        setupView()
    }

    override func awakeFromNib() {
        setupView()
    }

    private func setupView() {
        // Setup the layer
        layer.cornerRadius = bounds.height/2.0
        layer.masksToBounds = true

        // Setup the unfilled backgroundColor
        backgroundColor = UIColor(red: 249.0/255.0, green: 163.0/255.0, blue: 123.0/255.0, alpha: 1.0)

        // Setup filledView backgroundColor and add it as a subview
        fillView.backgroundColor = UIColor(red: 252.0/255.0, green: 95.0/255.0, blue: 95.0/255.0, alpha: 1.0)
        addSubview(fillView)

        // Update fillView frame in case coeff already has a value
        updateFillViewFrame()
    }

    private func updateFillViewFrame() {
        fillView.frame = CGRectMake(0, bounds.height*(1-coeff), bounds.width, bounds.height*coeff)
    }

    // Setter function to set the coeff animated. If setting it not animated isn't necessary at all, consider removing this func and animate updateFillViewFrame() in coeff didSet
    func setCoeff(coeff: CGFloat, animated: Bool) {
        if animated {
            UIView.animateWithDuration(4.0, animations:{ () -> Void in
                self.coeff = coeff
            })
        } else {
            self.coeff = coeff
        }
    }

}

在您的按钮回调中,您只需执行以下操作:

cell.pageBadgeView.setCoeff(1.0, animated: true)

答案 1 :(得分:3)

试试这个游乐场代码

import UIKit
import CoreGraphics

var str = "Hello, playground"

class BadgeView: UIView {

    var coeff:CGFloat = 0.5

    func drawCircleInView(){
    // Set up the shape of the circle
        let size:CGSize = self.bounds.size;
        let layer = CALayer();
        layer.frame = self.bounds;
        layer.backgroundColor = UIColor.blue().cgColor



        let initialRect:CGRect = CGRect.init(x: 0, y: size.height, width: size.width, height: 0)

        let finalRect:CGRect = CGRect.init(x: 0, y: size.height/2, width: size.width, height: size.height/2)

        let sublayer = CALayer()
        sublayer.frame = initialRect
        sublayer.backgroundColor = UIColor.orange().cgColor
        sublayer.opacity = 0.5


        let mask:CAShapeLayer = CAShapeLayer()
        mask.frame = self.bounds
        mask.path = UIBezierPath(ovalIn: self.bounds).cgPath
        mask.fillColor = UIColor.black().cgColor
        mask.strokeColor = UIColor.yellow().cgColor

        layer.addSublayer(sublayer)
        layer.mask = mask

        self.layer.addSublayer(layer)

        let boundsAnim:CABasicAnimation  = CABasicAnimation(keyPath: "bounds")
        boundsAnim.toValue = NSValue.init(cgRect:finalRect)

        let anim = CAAnimationGroup()
        anim.animations = [boundsAnim]
        anim.isRemovedOnCompletion = false
        anim.duration = 3
        anim.fillMode = kCAFillModeForwards
        sublayer.add(anim, forKey: nil)
    }
}

var badgeView:BadgeView = BadgeView(frame:CGRect.init(x: 50, y: 50, width: 50, height: 50))
var window:UIWindow = UIWindow(frame: CGRect.init(x: 0, y: 0, width: 200, height: 200))
window.backgroundColor = UIColor.red()
badgeView.backgroundColor = UIColor.green()
window.becomeKey()
window.makeKeyAndVisible()
window.addSubview(badgeView)
badgeView.drawCircleInView()

答案 2 :(得分:0)

对上述代码的更多修改,上面代码中缺少定位点代码

   ```
    var str = "Hello, playground"

 class BadgeView: UIView {

var coeff:CGFloat = 0.7

func drawCircleInView(){
    // Set up the shape of the circle
    let size:CGSize = self.bounds.size;

    let layerBackGround = CALayer();
    layerBackGround.frame = self.bounds;
    layerBackGround.backgroundColor = UIColor.blue.cgColor
   self.layer.addSublayer(layerBackGround)


    let initialRect:CGRect = CGRect.init(x: 0, y: size.height , width: size.width, height: 0)

    let finalRect:CGRect = CGRect.init(x: 0, y: 0, width: size.width, height:  size.height)

    let sublayer = CALayer()
    //sublayer.bounds = initialRect
    sublayer.frame = initialRect
    sublayer.anchorPoint = CGPoint(x: 0.5, y: 1)
    sublayer.backgroundColor = UIColor.orange.cgColor
    sublayer.opacity = 1


    let mask:CAShapeLayer = CAShapeLayer()
    mask.frame = self.bounds
    mask.path = UIBezierPath(ovalIn: self.bounds).cgPath
    mask.fillColor = UIColor.black.cgColor
    mask.strokeColor = UIColor.yellow.cgColor

    layerBackGround.addSublayer(sublayer)
    layerBackGround.mask = mask

    self.layer.addSublayer(layerBackGround)

    let boundsAnim:CABasicAnimation  = CABasicAnimation(keyPath: "bounds")
    boundsAnim.toValue = NSValue.init(cgRect:finalRect)

    let anim = CAAnimationGroup()
    anim.animations = [boundsAnim]
    anim.isRemovedOnCompletion = false
    anim.duration = 1
    anim.fillMode = kCAFillModeForwards
    sublayer.add(anim, forKey: nil)
}