iOS 图表:在饼图中以百分比显示实际值?

iOS Charts: Showing actual value with percentage inside PieChart?

我目前正在使用 Charts - by Daniel Gindi,按照他的演示,我实现了一个饼图,如下所示:

使用以下代码实现:

func createPieChart(dataPoints: [String: Double])
{
    for index in 0..<sortedDates.count
    {
        let year = sortedDates[index]

        guard let costs = dataPoints[year] else {
            return
        }

        let dataEntry = PieChartDataEntry(value: costs, label: year)

        dataEntries.append(dataEntry)
    }

    chartDataSet = PieChartDataSet(values: dataEntries, label: "")

    chartData = PieChartData(dataSet: chartDataSet)

    chartDataSet.sliceSpace = 2.0

    chartDataSet.yValuePosition = PieChartDataSet.ValuePosition.outsideSlice

    ...

    pieChartView.usePercentValuesEnabled = true
    pieChartView.drawSlicesUnderHoleEnabled = false
    pieChartView.holeRadiusPercent = 0.40
    pieChartView.transparentCircleRadiusPercent = 0.43
    pieChartView.drawHoleEnabled = true
    pieChartView.rotationAngle = 0.0
    pieChartView.rotationEnabled = true
    pieChartView.highlightPerTapEnabled = false

    let pieChartLegend = pieChartView.legend
    pieChartLegend.horizontalAlignment = Legend.HorizontalAlignment.right
    pieChartLegend.verticalAlignment = Legend.VerticalAlignment.top
    pieChartLegend.orientation = Legend.Orientation.vertical
    pieChartLegend.drawInside = false
    pieChartLegend.yOffset = 10.0

    pieChartView.legend.enabled = true

    pieChartView.data = chartData
}

我想要实现的是显示饼图切片内的实际 costs 值,但也包括切片外的百分比。使用 chartDataSet.yValuePosition = PieChartDataSet.ValuePosition.outsideSlice.

显示百分比

检查代码,我注意到在 PieChartRenderer 中有一个函数 open override func drawValues(context: CGContext) 并且在其中,代码计算 valueText 中每个饼图的百分比:

let valueText = formatter.stringForValue(
                    value,
                    entry: e,
                    dataSetIndex: i,
                    viewPortHandler: viewPortHandler)

然后使用 if 检查,绘制切片内部或外部的百分比,在我的例子中是外部:

else if drawYOutside
{
    ChartUtils.drawText(
                        context: context,
                        text: valueText,
                        point: CGPoint(x: 0, y: labelPoint.y + lineHeight / 2.0),
                        align: align,
                        attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: valueTextColor]
                        )
}

但是,我找不到实际包含传递给 PieChartDataEntry(value: costs, label: year)costs 值的方法。

我被告知要对饼图进行子类化并覆盖我找到的方法,但我不确定该怎么做。

例如,dataPoints中每个索引处的值costs是标签2017100.0和标签50.0的双重值标签 2016。我想将这两个值都包含在关联的饼图切片中。

任何有此库经验的人都可以帮助我吗?

谢谢!

您需要将 yValuePosition 属性 更改为 insideSlice

pieChartDataSet.yValuePosition = PieChartDataSet.ValuePosition.insideSlice

默认情况下它位于内部,但您指定为外部,因此它显示在图表外部。

编辑:

根据评论,请检查下面的代码,它在饼图中显示的是值而不是百分比

func setPieChartData(lables: [String]!, values:[Double]!) {

        var valuesArray: [PieChartDataEntry] = []
        var colors: [UIColor] = []
        let valNSArray = values as NSArray

        valNSArray.enumerateObjects({ object, index, stop in
            //your code
            colors.append(self.getRandomColor())


            valuesArray.append(PieChartDataEntry(value: object as! Double, label: lables[index]))

        })

        let pieChartDataSet = PieChartDataSet(values: valuesArray, label: "Pie Data Chart")
        pieChartDataSet.colors = colors

        let piechartData = PieChartData(dataSets:[pieChartDataSet])
        pieChartDataSet.yValuePosition = PieChartDataSet.ValuePosition.insideSlice
        self.data = piechartData



}

希望这会有所帮助。

创建一个继承自 PieChartRenderer 的 class 并覆盖父 class 的 drawValues,如下所示:

public class CustomClass: PieChartRenderer
{
    override public func drawValues(context: CGContext)
    {
        // In this body, copy ALL code from parent class drawValues function

        ...
        let decimalFormatter = NumberFormatter()
        decimalFormatter.minimumFractionDigits = 2
        decimalFormatter.maximumFractionDigits = 2
        decimalFormatter.positivePrefix = "$"

        ...

        for i in 0 ..< dataSets.count
        {
            ...

            for j in 0 ..< dataSet.entryCount
            {
                ...

                guard let e = dataSet.entryForIndex(j) else { continue }
                let pe = e as? PieChartDataEntry

                // e.y = the actual value in the costs array, not the percentage
                let twoDecimalValue = NSDecimalNumber(value: e.y)
                let totalCost = decimalFormatter.string(from: twoDecimalValue)

                ...

                if drawXInside || drawYInside
                {
                    ...

                    if drawXInside && drawYInside
                    {
                        ...
                    }
                    else if drawXInside
                    {
                        if j < data.entryCount && pe?.label != nil
                        {
                            ChartUtils.drawText(
                                context: context,
                                text: pe!.label!,
                                point: CGPoint(x: x,
                                               y: y),
                                align: .center,
                                attributes: [NSFontAttributeName: yearValueFont,
                                             NSForegroundColorAttributeName: yearTextColor ?? percentageTextColor]
                            )

                            ChartUtils.drawText(
                                context: context,
                                text: totalCost!,
                                point: CGPoint(x: x,
                                               y: y + lineHeight),
                                align: .center,
                                attributes: [NSFontAttributeName: yearCostsValueFont,
                                             NSForegroundColorAttributeName: yearCostsTextColor]
                            )
                        }
                    }
                }
            }
        }
    }
}

else if drawXInside 中,第一个 existing drawText 函数绘制 label 值,在我的例子中,。再添加一个drawText函数,传入totalCost值,代表数组`costs.

中的actual

然后在 createPieChart 函数中:

func createPieChart(dataPoints: [String: Double])
{
    ...

    pieChartView.data = chartData

    let customClass = CustomClass(chart: pieChartView, 
                                  animator: pieChartView.chartAnimator,
                                  viewPortHandler: pieChartView.viewPortHandler)
    pieChartView.renderer = customRenderer   
}

结果: