Charts: StackedBar value label overlapped

Created on 21 Dec 2015  ·  18Comments  ·  Source: danielgindi/Charts

i use stackedBar to show two data, but when one value is zero , it seems two label is overlapped.
e998a5b0-5e3f-49d6-800c-464d95934ef9

discussion enhancement

Most helpful comment

I checked Apple's documentation, found a way to solve our problems.
BarChartData have a ValueFormatter property, and there is a zeroSymbol NSNumberFormatter property, we can replace the 0 with an empty string. Here is the code.

    BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets];
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    formatter.maximumFractionDigits = 1;
    formatter.numberStyle = kCFNumberFormatterDecimalStyle;
    formatter.zeroSymbol  = @"";

    [data setValueFormatter:formatter];
    [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]];

@luomuwen @natanrolnik

All 18 comments

your chart seems weird, could you provide the sample code using ChartsDemo code?

I can see many 0 on left, and two 0 on right, and overlap with 1?

This is my code

    NSMutableArray *xVals = [[NSMutableArray alloc] init];
    for (int i = 0; i < 12; i++){
        [xVals addObject:self.months[i]];
    }

    NSMutableArray *yVals = [[NSMutableArray alloc] init];
    if(self.statisticsModel.monthStatisticsArray){
        for (int i = 0; i < [self.statisticsModel.monthStatisticsArray count]; i++){
            CVMonthStatisticsModel* monthModel = self.statisticsModel.monthStatisticsArray[i];

            CVLog(@"success:%d, fail:%d", monthModel.success, monthModel.fail);
            [yVals addObject:[[BarChartDataEntry alloc] initWithValues:@[@(monthModel.success), @(monthModel.fail)] xIndex:i]];
        }
    }

    BarChartDataSet *set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@""];
    set1.barSpace = 0.3;
    set1.colors = @[UIColorFromRGB(0x00b26d), UIColorFromRGB(0xfac955)];
    set1.stackLabels = @[@"成功", @"失败"];
    set1.valueTextColor = UIColorFromRGB(0x2CC9CA);

    NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
    formatter.numberStyle =  kCFNumberFormatterDecimalStyle;

    set1.valueFormatter = formatter;

    NSMutableArray *dataSets = [[NSMutableArray alloc] init];
    [dataSets addObject:set1];

    BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets];
    [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]];
    self.statisticView.data = data;

this is normal
aa4fc214-d5ed-4cd7-86b5-42d72bebb8d4

but when if one value is zero, the value labels will overlap

+1 I am having the same issue.
What is a way of disabling this label until the bug is fixed?

To temporarily disable it, just turn off dataSet.drawValuesEnabled

I looked into it today, and I found it's hard to call it as a bug.

This happens when you have one big value than others, so its bar takes most of the space.

Because the first value is, say 72.0, and the second value let's say it's 2.0, the 2.0 bar will be very thin, its width is even less than the 72.0 text width. By default, drawValueAboveBar is enabled, so it will draw 72.0 text on the right side (above first bar), occupying the second bar's space and the second text space and this leads to some overlap.

If the second value is 0.0, it's more obvious because the second bar's width will be 0. so the second bar's text will draw at the same point of the first text, which will be fully overlapped. There is no easy way to fix it, because the second bar is too thin to make some space.

We could somehow check the bar's neighbor to see if its width is enough for drawing the current bar's text, if not, we can skip drawing the neighbor's text.

Another way is, providing a flag to draw the value text in the middle of the bar, (and if the bar width is too small to hold its text, we can simply not drawing it)

what do you think @danielgindi

“We could somehow check the bar's neighbor to see if its width is enough for drawing the current bar's text, if not, we can skip drawing the neighbor's text.”

It is a good idea, like a map, when you zoom in, more details will be displayed; it displays only the main roads or landmarks when reduced.

Another problem is that, when in use stackBarView, like setting marker:

BalloonMarker * marker = [[BalloonMarker alloc] initWithColor: [UIColor colorWithWhite: 180/255 alpha: 1.0] font: [UIFont systemFontOfSize: 12.0] insets: UIEdgeInsetsMake (8.0, 8.0, 20.0, 8.0)];
marker.minimumSize = CGSizeMake (80.f, 40.f);
_barChart.marker = marker;

Click on each stack, marker display is always the sum, how to make the digital marker on a display of the value of each stack?

img_0288
img_0289

@hh1myfei you modify the balloon marker display value to whatever you need.

@liuxuan30 How to set up, I can not find a place to set up the marker value.

I checked Apple's documentation, found a way to solve our problems.
BarChartData have a ValueFormatter property, and there is a zeroSymbol NSNumberFormatter property, we can replace the 0 with an empty string. Here is the code.

    BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets];
    NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
    formatter.maximumFractionDigits = 1;
    formatter.numberStyle = kCFNumberFormatterDecimalStyle;
    formatter.zeroSymbol  = @"";

    [data setValueFormatter:formatter];
    [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]];

@luomuwen @natanrolnik

Seems like we can close this one?

@danielgindi I think it's not resolved yet. @hh1myfei just provide a solution when the value is 0.
However, quote from my post:
Because the first value is, say 72.0, and the second value let's say it's 2.0, the 2.0 bar will be very thin, its width is even less than the 72.0 text width. By default, drawValueAboveBar is enabled, so it will draw 72.0 text on the right side (above first bar), occupying the second bar's space and the second text space and this leads to some overlap.
So if it's not 0, the label will still overlap.

I'm currently running into the same issues. I put in a zero value check to get rid of the zero labels and try and check that the values do not overlap:

                            // Check that value won't write on top of another value
                            if k > 0 && vals[k - 1] != 0 &&
                                ((transformed[k - 1].y - transformed[k].y + posOffset) < 0)
                            {
                                continue
                            }

                            if vals[k] != 0 {
                                drawValue(context: context,
                                          value: formatter.string(from: vals[k] as NSNumber)!,
                                          xPos: x,
                                          yPos: y,
                                          font: valueFont,
                                          align: .center,
                                          color: dataSet.valueTextColorAt(j))
                            }

This seems to work ok for my dataset at least - not perfect results but ok.

Sample screenshot of dataset with zero value stacks and overlapping labels after above code in BarChartRenderer:

screenshot 2016-10-14 07 26 01

I guess the real solution will be check the segment width/height and the text width/hight to ignore some.

Yes, when I switched to values within the bar and check that the label will fit in bounds it looks even better :)

                               // Check that value won't write on top of another value above the bar
                                if drawValueAboveBar && k > 0 && vals[k - 1] != 0 &&
                                    ((transformed[k - 1].y - transformed[k].y + posOffset) < 0)
                                {
                                    continue
                                }

                                // Ensure that the value is contained within the bar
                                if !drawValueAboveBar {
                                    if k > 0 {
                                        if ((transformed[k - 1].y - transformed[k].y) + negOffset) < 0 {
                                            continue
                                        }
                                    } else {
                                        if (transformed[k].y + negOffset) < 0 {
                                            continue
                                        }
                                    }
                                }

                                if vals[k] != 0 {
                                    drawValue(context: context,
                                              value: formatter.string(from: vals[k] as NSNumber)!,
                                              xPos: x,
                                              yPos: y,
                                              font: valueFont,
                                              align: .center,
                                              color: dataSet.valueTextColorAt(j))
                                }

simulator screen shot 14 oct 2016 17 00 39

This is work for me fine.

BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets];
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
formatter.maximumFractionDigits = 1;
formatter.numberStyle = kCFNumberFormatterDecimalStyle;
formatter.zeroSymbol  = @"";

How to Autoscroll the chart to the current month. The current month should be visible to the user in front.

My problem is that when i run the program i am getting the output like this ,

screen shot 2016-10-20 at 12 22 09 pm

but i need to Autoscroll the chart to the current month.
screen shot 2016-10-20 at 12 22 28 pm

Thanks for the responses here, helped me to resolve this issue in my code, but this IS a bug!

You can add one Marker for your Chart, and add this implementation for that
Example in BallonMarker available in ChartsDemo.

open override func refreshContent(entry: ChartDataEntry, highlight: Highlight)
{
var value = entry.y
if let es = entry as? BarChartDataEntry {
if(highlight.isStacked){
if let valor = es.yValues?[highlight.stackIndex] {
value = valor
}
}
}
setLabel(value)
}

Was this page helpful?
0 / 5 - 0 ratings