Labels on top of Bars


#1

Hi there,

We would like to implement a simple BAR Chart but with one small thing. We would like to have a label with number on top of each bar.

This number is actual value for that bar. Please see the screenshot.

How we can do it?

The Second question: how I can recieve an event  when user clicks on AxisY label(2012,2011 on the screenshot)?

Thanks in advance,

Vladimir.


Positioning annotation in stacked bar chart
Vertical Stacked bar (Column Chart)
#2

I think they must have used annotations for this example project. Hopefully Shinobi will release all their examples as code in the next release!


#3

do you know whether the Annotation feature is already implemented?


#4
Vlad EE
do you know whether the Annotation feature is already implemented?

The Second question: how I can recieve an event  when user clicks on AxisY label(2012,2011 on the screenshot)?

I think this you can achieve by adding tap gesture to tickLabel property of SChartTickMark in this delegate method

- (void)sChart:(ShinobiChart *)chart alterTickMark:(SChartTickMark *)tickMark beforeAddingToAxis:(SChartAxis *)axis

Hope this helps :slight_smile:


#5
Vlad EE
do you know whether the Annotation feature is already implemented?

The SChartAnnotation class should do it - I haven’t tried it myself though.


#6

Hi Vlad, you can create an annotation and add it to the chart as follows…

// create an annotation
SChartAnnotation* an = [SChartAnnotation annotationWithText:@"foo"
                                                    andFont:[UIFont systemFontOfSize:20.f]
                                                  withXAxis:chart.xAxis
                                                   andYAxis:chart.yAxis
                                                atXPosition:@2.2
                                               andYPosition:@1
                                              withTextColor:[UIColor whiteColor]
                                        withBackgroundColor:[UIColor redColor]];
[chart addAnnotation:an];

You will have to create an annotation for each bar.

Adding data-labels is something we will probably add in a future release.

Colin E.


#7

What’s the best place to create/update these annotations if I want to achieve this effect?


#8

Since you’d need a label for each bar - a simple solution is to add the annotation at the same time as the data-point is sent to the the chart from the datasource (the dataPointAtIndex method).

However, simple isn’t always best and this means putting layout code in the datasource. I’ve modified the renderFinshed method on the BarChart sample app to add labels. It requires some modificaiton of the label frame to make it right aligned but should work just as well for columns with some adjustment (firstRender is a bool set in viewDidLoad to YES):

- (void)sChartRenderFinished:(ShinobiChart *)chart {
    BOOL iPad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad;
    
    //use multi-line title on the yAxis for iPad
    if (iPad) {
        chart.yAxis.titleLabel.numberOfLines = 0;
        [chart.yAxis.titleLabel sizeToFit];
    }
    
    if (firstRender) {
        firstRender = NO;
        
        SChartSeries *myBarSeries = [chart.chartSeries objectAtIndex:0];
        
        for (SChartDataPoint *dp in myBarSeries.dataSeries.dataPoints) {
            SChartAnnotation *a = [SChartAnnotation annotationWithText:[NSString stringWithFormat:@"%d",[dp.xValue intValue]]
                                                              andFont:nil
                                                            withXAxis:chart.xAxis
                                                             andYAxis:chart.yAxis
                                                          atXPosition:dp.xValue
                                                         andYPosition:dp.yValue
                                                        withTextColor:[UIColor blackColor]
                                                  withBackgroundColor:[UIColor clearColor]];
            
            [chart addAnnotation:a];
            CGRect f = a.label.frame;
            f.origin.x -= f.size.width/2.f;
            a.label.frame = f;
        }
        
    }
}

#9

Thanks, Stu. I’ve tried this with my column chart, and I see nothing. I’m guess that this is related to the bogus widths of my columns (covered here)?


#10

@Stu, I’ve implemented the code above, and all of my annotations seem to be placed at an origin of 0, 0. I’m guessing this only works with a bar chart, and not with columns…


#11

Hi Tony,

Annotations aren’t linked to series specifically, you just give them X and Y values with respect to axes. Are these values correct on your datapoint? Similiarly, are you passing the correct axes when creating the annotation? You can get the axes for a given series as follows:

SChartAxis *xAxis, *yAxis;
[chart axesForSeries: myBarSeries storeX: &xAxis andStoreY: &yAxis];

#12

Hi Simon, I was not — I’ve just added this code for each series in my chart to try and get the alignment right, and it’s aborting the app and logging:

2013-05-16 14:40:16.913 MyApp[12679:c07] Couldn’t find axis for series “<SChartColumnSeries: 0xeb70c80>”

More digging to be done on my end, I guess.


#13

It appears to be another method that calls abort() and terminates the entire app if there’s no data.

 (╯°□°)╯︵ ┻━┻


#14

Hey Tony,

When are you making that call? Is it in the renderFinished method as in Stu’s example? It sounds like you might be trying to get the axes before the chart has finished loading the series and linked them to the axes etc. Any chance you can let us see what you’re doing/some context? It’d really help me to diagnose the underlying problem :slight_smile:

Simon


#15

Hi Simon, yes, it’s in the renderFinished method the same as in Stu’s example. Is there a method that’s called after the series are linked to their axes?

Here’s the render finished method from my datasource class: https://gist.github.com/tonyarnold/b6a1c2b36a16929c1176


#16

Hi Tony,

I’ve tried to reproduce this problem without any luck. I’ve had to use a simplified version of your datasource as I don’t have access to some of your data for example, but this seems to add an annotation to my chart:

- (void)sChartRenderFinished:(ShinobiChart *)chart
{
    if (YES == self.firstChartRender) {
        self.firstChartRender = NO;
        
        [chart removeAllAnnotations];
        
        SChartCategoryAxis *categoryAxis = (SChartCategoryAxis *)chart.xAxis;
        
        __block NSInteger xIndex = NSNotFound;

        [categoryAxis.categories enumerateObjectsUsingBlock:^(NSNumber *obj, NSUInteger idx, BOOL *stop) {
                xIndex = idx;
        }];

        SChartAnnotation *newAnnotation = [SChartAnnotation annotationWithText:[NSString stringWithFormat:@"Test"] // Hours
                                                                       andFont:[UIFont systemFontOfSize:19.f]
                                                                     withXAxis:chart.xAxis
                                                                      andYAxis:chart.yAxis
                                                                   atXPosition:@(xIndex)
                                                                  andYPosition:@6
                                                                 withTextColor:[UIColor blackColor]
                                                           withBackgroundColor:[UIColor clearColor]];
        
        newAnnotation.position = SChartAnnotationAboveData;
        [chart addAnnotation:newAnnotation];
        newAnnotation.transform = CGAffineTransformMakeTranslation(-14.f, -6.f);
    }
}

My Result:

Could you confirm that the addAnnotation method is being called at the appropriate times? I’d recommend simplifying your sChartRenderFinished method and just adding a static annotation. Does that work? 

Regards,
Chris


#17

Hi Chris,

  1. Is sChartRenderFinished already available within the SChartDataSource protocol? or should we create it?
  2. I checked the sample project BarChart inside demos folder. There was a renderFinished method inside ViewController, but it wasn’t being used or called anywhere

#18

Hi,

If you look in the docs you can see that the method is already available here

Where have you put the breakpoint? I ran the demo app and it gets hit every time for me.

Regards,
Chris


#19

Sorry, extremely stupid of me. I forgot to set the delegate to self  :blush: :blush:

But the problem is, the chart is drawn as follows:

The question should have been, How to insert labels for each stack index?


#20

Hi hariharanb,

You can add annotations for each of your columns, like you have in the screenshot you supplied. However, the feature for mulitple stacks on the same X-axis value isn’t fully supported with annotations.

You can implement this yourself by looping through all your charts annotations using the “getAnnotations” method in the SChartDelegate method “sChartRenderFinished”. Then you can change its frame to move it up and to the left or right regarding on which column annotation it’s showing. Here is an example of how to do this:

for(SChartAnnotation *ann in [chart getAnnotations]){
            ann.frame = CGRectMake(ann.frame.origin.x - 20,
                           ann.frame.origin.y - 20,
                           ann.frame.size.width,
                           ann.frame.size.height);
}

Let me know how you get on.

Kind Regards,
Andrew Polkinghorn