I'm currently using Charts - by Daniel Gindi, and following his demo, I've implemented a PieChart like so:
It was achieve using the following code:
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
}
What I'm trying to achieve is to show the actual costs
value inside the pie slice, but also include the percentage outside the slice. The percentage is shown using chartDataSet.yValuePosition = PieChartDataSet.ValuePosition.outsideSlice
.
Inspecting the code, I noticed that in PieChartRenderer
, there is there function open override func drawValues(context: CGContext)
and within it, the code calculates the percentage of each pie slice in valueText
:
let valueText = formatter.stringForValue(
value,
entry: e,
dataSetIndex: i,
viewPortHandler: viewPortHandler)
Then using an if
check, it draws the percentage inside or outside the slice, which in my case is outside:
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]
)
}
However, I can't find a way to actual include the costs
value that is passed into PieChartDataEntry(value: costs, label: year)
.
I was told to subclass the Pie Chart and override the methods I found but I'm not exactly sure how to do that.
For example, the value of costs
at each index in dataPoints
is a double value of 100.0
for the label 2017
and 50.0
for the label 2016
. I would like to include both those values in the associated pie slice.
Can anyone experienced with this library assist me?
Thanks!
Create a class that inherits from the PieChartRenderer
and override the drawValues
from the parent class like so:
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]
)
}
}
}
}
}
}
}
Within the else if drawXInside
, the first existing drawText
function draws the label
value, in my case, the year. Add another drawText
function and pass in the totalCost
value, which represents the actual value in the array `costs.
Then within the createPieChart
function:
func createPieChart(dataPoints: [String: Double])
{
...
pieChartView.data = chartData
let customClass = CustomClass(chart: pieChartView,
animator: pieChartView.chartAnimator,
viewPortHandler: pieChartView.viewPortHandler)
pieChartView.renderer = customRenderer
}
The result: