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
是标签2017
的100.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
}
结果:
我目前正在使用 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
是标签2017
的100.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.
然后在 createPieChart
函数中:
func createPieChart(dataPoints: [String: Double])
{
...
pieChartView.data = chartData
let customClass = CustomClass(chart: pieChartView,
animator: pieChartView.chartAnimator,
viewPortHandler: pieChartView.viewPortHandler)
pieChartView.renderer = customRenderer
}
结果: