Reputation: 407
I'm trying to implement a real-time scatter plot using CorePlot 1.0 on an iPad with iOS 5.1. Things are working well with a couple of issues and one major exception - axis redraw.
When enough data is collected, I adjust the range in the plotSpace thus:
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)self.graph.defaultPlotSpace;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(self.graphMinX)
length:CPTDecimalFromFloat(self.graphRangeX)];
When I do this, the plots on the graph adjust as if the axis had changed, but the axis doesn't adjust - so the plots of the data display correctly against an incorrect axis. The axis will correctly update 5 seconds after I stop the data source.
I have reviewed the code in RealTimePlot (RTP) from the CorePlot iOS Plot Gallery and I can't find any significant difference (though one surely exists).
One difference between my code and RTP:
I capture the new data in a background GCD queue, which is then "distributed" by attaching it to a custom notification in [NSNotificationCenter defaultCenter]
Update: A simplified view of the architecture hierarchy is something like this:
TreatmentGraph
object (managing the CPTXYGraph
)TreatmentChannel
objects (each managing a CPTXYPlot
)The DetailViewController
has an observer for the data notification which looks like this:
- (void)dataArrived:(NSNotification *)notification
{
FVMonitoredSignal *sig = [notification object];
NSValue *currValue = [sig.dataPoints lastObject];
CGPoint point = [currValue CGPointValue];
[self.treatmentGraph addPoint:point toChannelWithIdentifier:sig.signalName];
dispatch_async(dispatch_get_main_queue(), ^{
[self.graphHostingView.hostedGraph reloadData];
});
return;
}
(Note that I force a reload of the data using a GCD post to the UI queue - the example in RTP did not seem to require this) This is a red flag, but of what?
Within the TreatmentGraph
we check whether a X Axis adjustment is needed and the data is dispatched to the appropriate TreatmentChannel
.
- (void)addPoint:(CGPoint)point toChannelWithIdentifier:(NSString *)identifier
{
// Check for a graph shift
if (point.x >= (self.graphMinX + self.graphRangeX))
{
[self shiftGraphX];
}
FVTreatmentChannel *channel = [self.channels objectForKey:identifier];
[channel addPoint:point];
return;
}
- (void)shiftGraphX
{
CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)self.graph.defaultPlotSpace;
plotSpace.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromFloat(self.graphMinX) length:CPTDecimalFromFloat(self.graphRangeX)];
}
My guess is the axis isn't updated until the main queue is idle, but since I'm already forcing a reload when new data arrives, I'm perplexed why the axis redraw doesn't happen then.
The TreatmentChannel
accepts the new data like this:
- (void)addPoint:(CGPoint)point
{
[self.plotData addObject:[NSValue valueWithCGPoint:point]]; // cache it
[self.plot insertDataAtIndex:self.plotData.count-1 numberOfRecords:1];
[self.plot reloadData];
}
Note that I'm using -insertDataAtIndex:numberOfRecords:
to add just the new data and calling -reloadData
specifically on the CPTXYPlot
. This is not causing a display update - it's not until the -reloadData
is called in the data notification handler in the DetailViewController
that I get a display update.
Questions:
Item 1 was addressed by making sure any updates to the axis and/or plotspace were wrapped to put them back on the GCD main queue.
Item 2 was addressed by wrapping the calls to -insertDataAtIndex:numberOfRecords:
allowed the removal of many of the -reloadData
calls that were bothering me.
Moral of the story: consider interaction with CorePlot equivalent to UIKit calls - in terms of making sure they all happen on the main queue.
Upvotes: 2
Views: 2018
Reputation: 27381
The axes should relabel and redraw whenever either of the plot space ranges change. When are you updating the plot space based on the new data?
You have to tell the plot that new data is available. -reloadData
is one way to do that, although for this application, there are faster ways to do it. The real time plot example uses -insertDataAtIndex:numberOfRecords:
and -deleteDataInIndexRange:
to add new points and remove ones that scroll out of view. This is faster than reloading all of the data for the plot every time something changes. Calling -reloadData
on the graph instead of the affected plot is even slower if you have more than one plot since it will reload the data for all of your plots.
Upvotes: 2