2

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:

  • SplitViewController
    • DetailViewController
      • TreatmentGraph object (managing the CPTXYGraph)
        • [Collection of] 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:

  1. What can I do to cause my axis to update in a more timely manner?
  2. Any clues why I don't get plots on my graph unless I force a reload when data arrives?

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.

4

1 に答える 1

2
  1. 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?

  2. 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.

于 2012-06-03T00:26:46.603 に答える