使用Core Graphics制作饼图

时间:2016-03-02 16:23:06

标签: ios swift core-graphics

到目前为止,我已经填了一个圆圈,并且有关于它的内容。我试图制作一个饼图,代表满意和不满意客户的数量并呈现它。我对CG非常陌生,并且想知道有人可以提出足够的代码给我一个想法或指导我。

我是否应该在底部圆圈表示满意客户的数量,然后在其上添加另一个圆圈以显示不满意的客户?我是以正确的方式接近它吗?

到目前为止,这是我的代码。

__builtin_popcount()

编辑

此外,现在我开始看到我可能需要根据不满意的客户总数以弧线覆盖我的圈子。如何根据人数增加或减少覆盖弧的大小?

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:49)

您想要使用CGContextAddArc()函数(Swift 3中的CGContext.addArc())。这将允许您通过为饼图的每个片段绘制弧来为饼图创建多个片段。

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

import UIKit

struct Segment {

    // the color of a given segment
    var color: UIColor

    // the value of a given segment – will be used to automatically calculate a ratio
    var value: CGFloat
}

class PieChartView: UIView {

    /// An array of structs representing the segments of the pie chart
    var segments = [Segment]() {
        didSet {
            setNeedsDisplay() // re-draw view when the values get set
        }
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        isOpaque = false // when overriding drawRect, you must specify this to maintain transparency.
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
    }

    override func draw(_ rect: CGRect) {

        // get current context
        let ctx = UIGraphicsGetCurrentContext()

        // radius is the half the frame's width or height (whichever is smallest)
        let radius = min(frame.size.width, frame.size.height) * 0.5

        // center of the view
        let viewCenter = CGPoint(x: bounds.size.width * 0.5, y: bounds.size.height * 0.5)

        // enumerate the total value of the segments by using reduce to sum them
        let valueCount = segments.reduce(0, {$0 + $1.value})

        // the starting angle is -90 degrees (top of the circle, as the context is flipped). By default, 0 is the right hand side of the circle, with the positive angle being in an anti-clockwise direction (same as a unit circle in maths).
        var startAngle = -CGFloat.pi * 0.5

        for segment in segments { // loop through the values array

            // set fill color to the segment color
            ctx?.setFillColor(segment.color.cgColor)

            // update the end angle of the segment
            let endAngle = startAngle + 2 * .pi * (segment.value / valueCount)

            // move to the center of the pie chart
            ctx?.move(to: viewCenter)

            // add arc from the center for each segment (anticlockwise is specified for the arc, but as the view flips the context, it will produce a clockwise arc)
            ctx?.addArc(center: viewCenter, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)

            // fill segment
            ctx?.fillPath()

            // update starting angle of the next segment to the ending angle of this segment
            startAngle = endAngle
        }
    }
}

您可以将饼图数据输入为Segment个结构数组,其中每个Segment代表该细分的颜色和值。

该值可以是任何浮点数,并将自动降低到饼图中使用的比率。例如,如果您希望饼图表示未满足的客户数量与满意客户数量的比较,您可以直接传递这些值。

使用示例:

let pieChartView = PieChartView()
pieChartView.frame = CGRect(x: 0, y: 0, width: view.frame.size.width, height: 400)
pieChartView.segments = [
    Segment(color: .red, value: 57),
    Segment(color: .blue, value: 30),
    Segment(color: .green, value: 25),
    Segment(color: .yellow, value: 40)
]
view.addSubview(pieChartView)

<强>输出:

enter image description here

完整项目(具有一些额外功能):https://github.com/hamishknight/Pie-Chart-View