ShinobiCharts: assertion failed: !_verticesVBO->getDataStore().dirty() message when displaying charts


#1

Hi,

 

When trying to use a custom subclass of ShinobiChart, I am seeing this error message in the console for each instance of the subclass that is added and drawn on the screen:

 

ShinobiCharts: assertion failed: !_verticesVBO->getDataStore().dirty()

 

The chart subclass is its own delegate and datasource, only uses one line series, and is set up similarly to other subclasses of ShinobiChart in the app, none of which are displaying this message.  It seems to happen regardless of the number of data points or contents of the data points that are displayed.  The message only appears when the chart is actually drawn on the screen- I can configure it and go through the SChartDataSource methods without seeing any warning or error messages.

 

The charts are drawn correctly and look fine.  They are being drawn in collection view cells and scrolling performance is not ideal on an iPad 2, but I haven’t determined if this is causing the scrolling performance issues or if something else is yet.

 

I’m not sure what could be causing this, has anyone else run across a similar issue?


#2

Forgot to mention- this is Objective C.


#3

Greetings Program!

I took the GettingStarted sample app and subclassed the ShinobiChart leaving only the viewDidLoad to initialize and add the chart to a container view that I added to a view controller in IB.

I wasn’t able to recreate the warning on the 5s or iPad 2 simulator with target iOS 9.0.

Since you have other subclasses that are not affected, could it be something specific in this class that is different from the others?

Wg


#4

I think the biggest difference between the two classes is the way data is stored and loaded.  The class that does not have the error is structured like this:

@implementation SubclassOfShinobiChart0

NSArray *dataToDisplay;
other class variables

- (id)initWithFrame:(CGRect)frame {
method implementation
}

other class methods

- (void)refreshChartWithData:(NSArray*)dataArray{
    if (!dataArray){
        // in the absence of data, do nothing
        return;
    }
    dataToDisplay = dataArray;
    [self reloadData];
    [self redrawChart];
}

SChartDataSource methods

- (id<SChartData>)sChart:(ShinobiChart *)chart dataPointAtIndex:(NSInteger)dataIndex forSeriesAtIndex:(NSInteger)seriesIndex
{
    SChartDataPoint *datapoint = [[SChartDataPoint alloc] init];
    
    NSDictionary *pointDict = (NSDictionary*)dataToDisplay[dataIndex];

    datapoint.xValue = pointDict[@"key0"];
    datapoint.yValue = pointDict[@"key1"];
    
    return datapoint;
}

The data is stored in a class variable and is updated when the refreshChartWithData method is called.  I believe that the data for this class is always preloaded from a service before the chart’s view makes it on-screen and the refreshChartWithData call is made from the main thread.

This differs from the chart that is giving me trouble:

SubclassOfShinobiChart1.h

@interface SubclassOfShinobiChart1 ( )

@property (strong, nonatomic) NSArray *dataToDisplay;

@end

SubclassOfShinobiChart1.m

@implementation SubclassOfShinobiChart1

- (void)setdataToDisplay:(NSArray *)dataArray
{
    _dataToDisplay = dataArray;
    
    double minY = DBL_MAX;
    double maxY = DBL_MIN;
    for ( NSDictionary *dict in _dataToDisplay ) {
        minY = MIN(minY, [dict[@"key0"] doubleValue]);
        maxY = MAX(maxY, [dict[@"key1"] doubleValue]);
    }
    _yDelta = [NSNumber numberWithDouble:(maxY - minY)];
    
    self.yAxis.rangePaddingHigh = [NSNumber numberWithDouble:[self.yDelta doubleValue] * 0.35];
    self.yAxis.rangePaddingLow = [NSNumber numberWithDouble:[self.yDelta doubleValue] * 0.5];
    
    [super reloadData];
}

SChartDataSource methods

- (id<SChartData>)sChart:(ShinobiChart *)chart dataPointAtIndex:(NSInteger)dataIndex forSeriesAtIndex:(NSInteger)seriesIndex
{
    SChartDataPoint *dataPoint = [[SChartDataPoint alloc] init];
    
    NSDictionary *pointDict = [[NSDictionary alloc] init];
    
    if ( self.dataToDisplay ) {
        pointDict = (NSDictionary*)self.dataToDisplay[dataIndex];
        
        dataPoint.xValue = pointDict[@"key0"];
        dataPoint.yValue = pointDict[@"key1"];
        if ( dataIndex == (self.dataToDisplay.count - 2) ) {
            dataPoint.selected = YES;
        }
    } else {
        dataPoint.xValue = @0;
        dataPoint.yValue = @0;
    }
    
    return dataPoint;
}

The data is stored in a property of the the ShinobiChart subclass.  This chart is put on the screen before data is available and the dataToDisplay array is set in a return block from a service call off the main thread.

Another difference is that SubclassOfShinobiChart1 is a subview in an .xib file while SubclassOfShinobiChart2 is I believe set up completely in code.


#5

When you say SubclassOfShinobiChart1 is a subview in an .xib file - you mean SubclassOfShinobiChart0, correct? In your code, your class names end with 0 and 1.

The nib is a UICollectionViewCell, correct?

Wg


#6

This was a known issue and has since been fixed in release 2.8.3. Please give version 2.8.5 a try and let us know how this works for you.

Thanks,

Matt Webber


#7

We’ll try the update Matt, thanks.

WG, SubclassOfShinobiChart1 was in an xib that was loaded into a collection view cell (it’s a reuseable component that is used elsewhere in the app).  But we will try updating and see if that works.

Thanks guys!