Swift – 彩色填充animation

我对iosanimation比较陌生,我相信我用UIViewanimation的方法有些问题。

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

有一个tableView细胞有两个标签和丰富多彩的实心圆 在这里输入图像说明

任何时候,我都会为这个单元格引入新的价值,我想为这个最左边的灯泡制作animation,看起来它已经被填满了红色。

这是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方法里面,我尝试使用带callback的自定义button来animation这个形状

 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环境variables。

尽pipe我很想知道正确的答案和方法,但为了教育的目的,知道为什么这个代码不起作用,这是非常好的。 提前致谢

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

 cell.pageBadgeView.drawRect(frame) 

从UIView drawRect的Apple文档 :

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

所以,如果你想改变你的代码:

 cell.pageBadgeView.setNeedsDisplay() 

你会摆脱错误,并看到正确填写 badgeView。 然而, 这不会animation ,因为drawRect在默认情况下不是animation的。

对于你的问题最简单的解决方法是为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 } } } 

在你的buttoncallback中,你只需要做:

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

试试这个游乐场代码

 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() 

对上面的代码进行更多的修改,在上面的代码中缺less锚点代码

  ``` 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) }