5
votes

For PieCharts, there are often several "slices" with a very small portion of the data (< 2%, for example), and as a result, the labels overlap each other and are unreadable, displayed below. Does anyone know solutions to this? I've seen some charts that show the label outside the pie and point to the respective piece, but I'm not sure if something similar is possible for ios-charts.

enter image description here

A similar issue occurs for bar charts, in which all the values overlap with each other and become unreadable, shown below. The solution I can think of, would be to show only a subset of the bars, and show the other bars if the user pans.

enter image description here

If anyone has dealt with either issue, I'd appreciate seeing how you solved it, or if it's better to use a different library. I posted some code below, but not sure if it''ll help, since this presumably isn't really an implementation bug.

Bar Chart

    //...init view...
    _chartView.delegate = self;
    _chartView.descriptionText = @"";
    _chartView.noDataTextDescription = @"No data";
    _chartView.drawHighlightArrowEnabled = true;
    _chartView.drawValueAboveBarEnabled = YES;
    _chartView.drawMarkers = true;
    _chartView.dragEnabled = true;


    ChartXAxis *xAxis = _chartView.xAxis;
    xAxis.labelPosition = XAxisLabelPositionBottom;
    xAxis.labelFont = [UIFont systemFontOfSize:10.f];
    xAxis.drawGridLinesEnabled = NO;
    xAxis.spaceBetweenLabels = 2.0;

    ChartYAxis *rightAxis = _chartView.rightAxis;
    rightAxis.enabled = NO;

    NSMutableArray *yVals = [[NSMutableArray alloc] init];
    NSMutableArray *xVals = [[NSMutableArray alloc] init];

    //populate xVals and yVals with data..

    BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"Occurences"];
    set1.colors = ChartColorTemplates.vordiplom;
    set1.drawValuesEnabled = YES;

    NSMutableArray *dataSets = [[NSMutableArray alloc] init];
    [dataSets addObject:set1];
    BarChartData *bdata = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets];
    [bdata setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]];

    _chartView.data = bdata;

    [self.view addSubview:_chartView];

Pie Chart

//init pie chart.. _pieChart.delegate = self;

_pieChart.usePercentValuesEnabled = YES;
_pieChart.descriptionText = @"";
_pieChart.drawCenterTextEnabled = YES;

NSMutableArray *yVals = [[NSMutableArray alloc] init];
NSMutableArray *xVals = [[NSMutableArray alloc] init];

//...populate xVals and yVals with data 

PieChartDataSet *dataSet = [[PieChartDataSet alloc] initWithYVals:yVals label:@"Locations"];
dataSet.sliceSpace = 2.0;

dataSet.colors = ChartColorTemplates.joyful;
PieChartData *data1 = [[PieChartData alloc] initWithXVals:xVals dataSet:dataSet];
NSNumberFormatter *pFormatter = [[NSNumberFormatter alloc] init];
pFormatter.numberStyle = NSNumberFormatterPercentStyle;
pFormatter.maximumFractionDigits = 1;
pFormatter.multiplier = @1.f;
pFormatter.percentSymbol = @" %";
[data1 setValueFormatter:pFormatter];
_pieChart.data = data1;
[self.view addSubview:_pieChart];
2

2 Answers

0
votes

First, I would suggest file an issue on github page, and provide the sample code using ChartsDemo to reproduce. I tried it, and I do see small overlap, but if the bars are continuing smaller, then value will not be drawn. So the problem is the granularity.

Whether to draw text is based on:

internal func passesCheck() -> Bool
{
    guard let dataProvider = dataProvider, barData = dataProvider.barData else { return false }

    return CGFloat(barData.yValCount) < CGFloat(dataProvider.maxVisibleValueCount) * viewPortHandler.scaleX
}

If the values are too many, it won't draw; But it seems not enough granularity for you.

There are ways to solve it, for example bar chart, you can calculate the text width, and compare it with the bar pixel width to determine draw the text or not. You can override in drawValues or if you need to solve it in a hurry. Pie chart is similar.

Like:

// calculate bar pixel width
let barWidth: CGFloat = 0.5
let barSpaceHalf = dataSet.barSpace / 2.0
let left = 0 - barWidth + barSpaceHalf
let right = 0 + barWidth - barSpaceHalf

var leftPoint = CGPoint(x: left, y: 0)
var rightPoint = CGPoint(x: right, y: 0)
trans.pointValueToPixel(&leftPoint)
trans.pointValueToPixel(&rightPoint)

let barPixelWidth = rightPoint.x - leftPoint.x
let valueTextWidth = valueText.sizeWithAttributes([NSFontAttributeName: valueFont]).width

if valueTextWidth < barPixelWidth
{
    // draw text
}
0
votes

Basically, you could calculate a middle point on your view (or CALayer, whatever is used) and draw the text, properly scaled for the figure:

NSString *dataString = [[NSString alloc]
    initWithFormat:@"Some data"];
CGPoint centerPoint =
    CGPointMake(self.bounds.origin.x + self.bounds.size.width / 2,
                self.bounds.origin.y + self.bounds.size.height / 2);

CATextLayer *textLayer = [CATextLayer layer];
[textLayer setFont:@"Helvetica-Bold"];

//Scale the font and set proper alignment 
[textLayer setFontSize:12];
[textLayer setAlignmentMode:kCAAlignmentCenter];
[textLayer setBounds:self.frame];

[textLayer setPosition:centerPoint];
[textLayer setString:dataString];   
[self.layer addSublayer:textLayer];