Swift 3: CAShapeLayer mask 没有为饼图动画制作动画
Swift 3: CAShapeLayer mask not animating for pie chart animation
我有代码可以在给定任意输入数据的情况下成功创建饼图。它生成一个如下所示的饼图:
我想要的:
我想创建一个动画,按顺序绘制饼图的这些部分。也就是说,我希望执行饼图的 "clock swipe" 动画。屏幕应该从黑色开始,然后逐渐填充以产生上面的饼图图像。
问题:
我正在尝试为每个饼图块创建一个图层蒙版并为图层蒙版制作动画,以便饼图块填充动画。但是,当我实现图层蒙版时,它没有动画并且屏幕保持黑色。
代码:
class GraphView: UIView {
// Array of colors for graph display
var colors: [CGColor] = [UIColor.orange.cgColor, UIColor.cyan.cgColor, UIColor.green.cgColor, UIColor.blue.cgColor]
override func draw(_ rect: CGRect) {
drawPieChart()
}
func drawPieChart() {
// Get data from file, yields array of numbers that sum to 100
let data: [String] = getData(from: "pie_chart")
// Create the pie chart
let pieCenter: CGPoint = CGPoint(x: frame.width / 2, y: frame.height / 2)
let radius = frame.width / 3
let rad = 2 * Double.pi
var lastEnd: Double = 0
var start: Double = 0
for i in 0...data.count - 1 {
let size = Double(data[i])! / 100
let end: Double = start + (size * rad)
// Create the main layer
let path = UIBezierPath()
path.move(to: pieCenter)
path.addArc(withCenter: pieCenter, radius: radius, startAngle: CGFloat(start), endAngle: CGFloat(end), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = colors[i]
// Create the mask
let maskPath = CGMutablePath()
maskPath.move(to: pieCenter)
maskPath.addArc(center: pieCenter, radius: radius, startAngle: CGFloat(start), endAngle: CGFloat(end), clockwise: true)
let shapeLayerMask = CAShapeLayer()
shapeLayerMask.path = maskPath
shapeLayerMask.fillColor = colors[i]
shapeLayerMask.lineWidth = radius
shapeLayerMask.strokeStart = 0
shapeLayerMask.strokeEnd = 1
// Set the mask
shapeLayer.mask = shapeLayerMask
shapeLayer.mask!.frame = shapeLayer.bounds
// Add the masked layer to the main layer
self.layer.addSublayer(shapeLayer)
// Animate the mask
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = shapeLayerMask.strokeStart
animation.toValue = shapeLayerMask.strokeEnd
animation.duration = 4
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.isRemovedOnCompletion = false
animation.autoreverses = false
shapeLayerMask.add(animation, forKey: nil)
}
start = end
}
}
}
同样,这段代码只会产生一个空白屏幕。如果你注释掉遮罩相关代码并添加 shapeLayer
作为子视图,你将看到非动画饼图。
我哪里做错了,我怎样才能让我的饼图动画化?
必须先将子图层添加到父图层,然后才能对其进行遮罩:
// Code above this line adds the pie chart slices to the parentLayer
for layer in parentLayer.sublayers! {
// Code in this block creates and animates a mask for each layer
}
// Add the parent layer to the highest parent view/layer
仅此而已。您必须确保饼图切片和子图层之间存在 一对一对应关系 。
这样做的原因是因为您不能使用一个图层蒙版实例来蒙版多个 views/layers。执行此操作时,图层蒙版将成为您尝试蒙版的图层 Layer Hierarchy 的一部分。必须在遮罩和要遮罩的层之间建立父子关系才能使其工作。
我有代码可以在给定任意输入数据的情况下成功创建饼图。它生成一个如下所示的饼图:
我想要的:
我想创建一个动画,按顺序绘制饼图的这些部分。也就是说,我希望执行饼图的 "clock swipe" 动画。屏幕应该从黑色开始,然后逐渐填充以产生上面的饼图图像。
问题:
我正在尝试为每个饼图块创建一个图层蒙版并为图层蒙版制作动画,以便饼图块填充动画。但是,当我实现图层蒙版时,它没有动画并且屏幕保持黑色。
代码:
class GraphView: UIView {
// Array of colors for graph display
var colors: [CGColor] = [UIColor.orange.cgColor, UIColor.cyan.cgColor, UIColor.green.cgColor, UIColor.blue.cgColor]
override func draw(_ rect: CGRect) {
drawPieChart()
}
func drawPieChart() {
// Get data from file, yields array of numbers that sum to 100
let data: [String] = getData(from: "pie_chart")
// Create the pie chart
let pieCenter: CGPoint = CGPoint(x: frame.width / 2, y: frame.height / 2)
let radius = frame.width / 3
let rad = 2 * Double.pi
var lastEnd: Double = 0
var start: Double = 0
for i in 0...data.count - 1 {
let size = Double(data[i])! / 100
let end: Double = start + (size * rad)
// Create the main layer
let path = UIBezierPath()
path.move(to: pieCenter)
path.addArc(withCenter: pieCenter, radius: radius, startAngle: CGFloat(start), endAngle: CGFloat(end), clockwise: true)
let shapeLayer = CAShapeLayer()
shapeLayer.path = path.cgPath
shapeLayer.fillColor = colors[i]
// Create the mask
let maskPath = CGMutablePath()
maskPath.move(to: pieCenter)
maskPath.addArc(center: pieCenter, radius: radius, startAngle: CGFloat(start), endAngle: CGFloat(end), clockwise: true)
let shapeLayerMask = CAShapeLayer()
shapeLayerMask.path = maskPath
shapeLayerMask.fillColor = colors[i]
shapeLayerMask.lineWidth = radius
shapeLayerMask.strokeStart = 0
shapeLayerMask.strokeEnd = 1
// Set the mask
shapeLayer.mask = shapeLayerMask
shapeLayer.mask!.frame = shapeLayer.bounds
// Add the masked layer to the main layer
self.layer.addSublayer(shapeLayer)
// Animate the mask
DispatchQueue.main.asyncAfter(deadline: .now() + Double(i)) {
let animation = CABasicAnimation(keyPath: "strokeEnd")
animation.fromValue = shapeLayerMask.strokeStart
animation.toValue = shapeLayerMask.strokeEnd
animation.duration = 4
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.isRemovedOnCompletion = false
animation.autoreverses = false
shapeLayerMask.add(animation, forKey: nil)
}
start = end
}
}
}
同样,这段代码只会产生一个空白屏幕。如果你注释掉遮罩相关代码并添加 shapeLayer
作为子视图,你将看到非动画饼图。
我哪里做错了,我怎样才能让我的饼图动画化?
必须先将子图层添加到父图层,然后才能对其进行遮罩:
// Code above this line adds the pie chart slices to the parentLayer
for layer in parentLayer.sublayers! {
// Code in this block creates and animates a mask for each layer
}
// Add the parent layer to the highest parent view/layer
仅此而已。您必须确保饼图切片和子图层之间存在 一对一对应关系 。 这样做的原因是因为您不能使用一个图层蒙版实例来蒙版多个 views/layers。执行此操作时,图层蒙版将成为您尝试蒙版的图层 Layer Hierarchy 的一部分。必须在遮罩和要遮罩的层之间建立父子关系才能使其工作。