我前几天刚刚发现了核心情节,并且一直试图在我们当前的应用程序(使用ARC)中实现它,以获得具有多条数据线的实时线图。但是,当找到并添加新数据点时,我似乎无法使图形向右滚动。相反,我看到数据线偏离图表的右侧。我试图按照Plot Gallery应用程序(Real Time Plot);无济于事。我看过这里的其他问题类似于我的问题,但没有一个答案似乎解决了我的问题。
这就是我正在做的事情:
我有一个自定义的UIViewController附加到我的故事板中的对象。以下是相关代码:
@interface RealTimeSignalsViewController : UIViewController <CPTPlotDataSource, CPTLegendDelegate, MCPacketProtocol>
{
//Key equals the ID of the line, value is MCPlotDataObject.
NSMutableDictionary* plotData;
BOOL newPacketReceived;
CPTGraphHostingView* hostView;
}
的.m
#define SIG_A_ID @"SIG_A"
#define SIG_B_ID @"SIG_B"
-(void)viewWillAppear:(BOOL)animated
{
newPacketReceived = NO;
NSString* curDev = [[Model instance] cur_deviceSpeakingWith];
plotData = [[NSMutableDictionary alloc] init];
[plotData setObject:[[MCPlotDataObject alloc] initWithID:[NSString stringWithFormat:SIG_A_ID]] forKey:[NSString stringWithFormat:SIG_A_ID]];
//If a second channel is detected
{
[plotData setObject:[[MCPlotDataObject alloc] initWithID:[NSString stringWithFormat:SIG_B_ID]] forKey:[NSString stringWithFormat:SIG_B_ID]];
}
[self configureHost];
[self configureGraph];
[self configurePlots];
}
- (void)viewDidLoad
{
[[DeviceHandler sharedInstance] addPacketDelegate:self];
[self checkForNewPackets];
[super viewDidLoad];
// Do any additional setup after loading the view.
}
-(void)configureHost
{
CGRect hvRect = [[self view] frame];
hvRect.size.height -= 150;
hvRect.size.width -= 50;
hvRect.origin.x += 25;
hvRect.origin.y += 75;
hostView = [(CPTGraphHostingView *) [CPTGraphHostingView alloc] initWithFrame:hvRect];
[[self view] addSubview:hostView];
}
-(void)configureGraph
{
CPTGraph* graph = [[CPTXYGraph alloc] initWithFrame:[hostView bounds]];
[graph applyTheme:[CPTTheme themeNamed:kCPTSlateTheme]];
[hostView setHostedGraph:graph];
graph.plotAreaFrame.paddingBottom = 40;
graph.plotAreaFrame.paddingLeft = 40;
graph.plotAreaFrame.paddingRight = 40;
graph.plotAreaFrame.paddingTop = 40;
CPTXYPlotSpace* space = (CPTXYPlotSpace*)[graph defaultPlotSpace];
CPTMutableLineStyle *majorGridLineStyle = [CPTMutableLineStyle lineStyle];
[majorGridLineStyle setLineWidth:0.75];
[majorGridLineStyle setLineColor:[[CPTColor colorWithGenericGray:0.2] colorWithAlphaComponent:0.75]];
CPTMutableLineStyle *minorGridLineStyle = [CPTMutableLineStyle lineStyle];
[minorGridLineStyle setLineWidth:0.25];
[minorGridLineStyle setLineColor:[[CPTColor whiteColor] colorWithAlphaComponent:0.1]];
// Axes
// X axis
CPTXYAxisSet* axisSet = (CPTXYAxisSet *)[graph axisSet];
CPTXYAxis* x = [axisSet xAxis];
[x setLabelingPolicy:CPTAxisLabelingPolicyNone];
[x setMajorGridLineStyle:majorGridLineStyle];
[x setMinorGridLineStyle:minorGridLineStyle];
[x setPlotSpace:[graph defaultPlotSpace]];
// Y axis
NSSet *majorTickLocations = [NSSet setWithObjects:[NSDecimalNumber zero],
[NSDecimalNumber numberWithUnsignedInteger:10],
[NSDecimalNumber numberWithUnsignedInteger:20],
[NSDecimalNumber numberWithUnsignedInteger:30],
[NSDecimalNumber numberWithUnsignedInteger:40],
[NSDecimalNumber numberWithUnsignedInteger:50],
[NSDecimalNumber numberWithUnsignedInteger:60],
[NSDecimalNumber numberWithUnsignedInteger:70],
[NSDecimalNumber numberWithUnsignedInteger:80],
[NSDecimalNumber numberWithUnsignedInteger:90],
[NSDecimalNumber numberWithUnsignedInteger:100],
nil];
CPTXYAxis *y = [axisSet yAxis];
[y setPlotSpace:[graph defaultPlotSpace]];
[y setLabelingPolicy:CPTAxisLabelingPolicyNone];
[y setOrthogonalCoordinateDecimal:CPTDecimalFromUnsignedInteger(0)];
[y setMajorGridLineStyle:majorGridLineStyle];
[y setMinorGridLineStyle:minorGridLineStyle];
[y setMinorTicksPerInterval:4];
[y setLabelOffset:5.0];
[y setMajorTickLocations:majorTickLocations];
[y setAxisConstraints:[CPTConstraints constraintWithLowerOffset:0.0]];
CPTMutableTextStyle *axisTitleTextStyle = [CPTMutableTextStyle textStyle];
[axisTitleTextStyle setColor:[CPTColor blackColor]];
axisTitleTextStyle.fontName = @"Helvetica-Bold";
axisTitleTextStyle.fontSize = 14.0;
NSMutableSet *newAxisLabels = [NSMutableSet set];
for(id n in majorTickLocations)
{
CPTAxisLabel *newLabel = [[CPTAxisLabel alloc] initWithText:[NSString stringWithFormat:@"%lu", (unsigned long)[n unsignedIntegerValue]] textStyle:axisTitleTextStyle];
NSDecimal loc = CPTDecimalFromUnsignedInt([n unsignedIntegerValue]); [newLabel setTickLocation:loc];
[newLabel setOffset:[y labelOffset] + [y majorTickLength] / 2.0];
if(newLabel)
[newAxisLabels addObject:newLabel];
}
[y setAxisLabels:newAxisLabels];
[space setYRange:[CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInt(0) length:CPTDecimalFromUnsignedInt(100)]];
space.xRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInteger(0) length:CPTDecimalFromUnsignedInteger([MCPlotDataObject getMaxDataPoints] - 2)];
}
-(void)configurePlots
{
CPTGraph* graph = [hostView hostedGraph];
//Keep track of the colors used for data lines
NSMutableArray* unusedKeys = [[NSMutableArray alloc] init];
for(NSString* keyID in plotData)
{
CPTScatterPlot* plot = (CPTScatterPlot*)[graph plotWithIdentifier:keyID];
if(plot)
{
//Mark off color used
}
else
{
[unusedKeys addObject:keyID];
}
}
for(NSString* keyID in unusedKeys)
{
CPTScatterPlot* linePlot = [[CPTScatterPlot alloc] init];
[linePlot setIdentifier:keyID];
[linePlot setCachePrecision:CPTPlotCachePrecisionDouble];
CPTMutableLineStyle *lineStyle = [linePlot.dataLineStyle mutableCopy];
[lineStyle setLineWidth:1.0];
//Assign a unique color
[linePlot setDataLineStyle:lineStyle];
[linePlot setDataSource:self];
[graph addPlot:linePlot];
}
CPTLegend* legend = [CPTLegend legendWithGraph:graph];
[legend setFill:[[graph plotAreaFrame] fill]];
[legend setBorderLineStyle:[[graph plotAreaFrame] borderLineStyle]];
[legend setCornerRadius:5.0];
[legend setSwatchSize:CGSizeMake(25.0, 25.0)];
[legend setSwatchCornerRadius:5.0];
[graph setLegendAnchor:CPTRectAnchorBottom];
[graph setLegendDisplacement:CGPointMake(0.0, 0.0)];
[graph setLegend:legend];
[[graph legend] setDelegate:self];
}
-(void)checkForNewPackets
{
if(newPacketReceived)
{
[self updateGraph];
newPacketReceived = false;
}
[self performSelector:@selector(checkForNewPackets) withObject:nil afterDelay:.01];
}
-(NSUInteger)numberOfRecordsForPlot:(CPTPlot *)plot
{
NSString* plotID = (NSString*)[plot identifier];
return [[(MCPlotDataObject*)[plotData objectForKey:plotID] points] count];
}
-(NSNumber *)numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index
{
NSNumber *num = nil;
NSString* plotID = (NSString*)[plot identifier];
MCPlotDataObject* mcpd = (MCPlotDataObject*)[plotData objectForKey:plotID];
switch(fieldEnum)
{
case CPTScatterPlotFieldX:
num = [NSNumber numberWithUnsignedInteger:index + [mcpd currrentIndex] - [[mcpd points] count]];
break;
case CPTScatterPlotFieldY:
num = [[mcpd points] objectAtIndex:index];
break;
default:
break;
}
return num;
}
-(void)updateGraph
{
CPTGraph* graph = [hostView hostedGraph];
CPTXYPlotSpace* plotSpace = (CPTXYPlotSpace*)[graph defaultPlotSpace];
NSUInteger curIndex = 0;
NSUInteger maxPoints = [MCPlotDataObject getMaxDataPoints];
NSUInteger location = 0;
//Delete the unnecessary points from each plot line, get the curIndex, maxPoints, and location using the first keyID
for(NSString* keyID in plotData)
{
MCPlotDataObject* mcpd = (MCPlotDataObject*)[plotData objectForKey:keyID];
CPTPlot* linePlot = [graph plotWithIdentifier:keyID];
if([mcpd removePlotPoint])
{
[linePlot deleteDataInIndexRange:NSMakeRange(0, 1)];
}
if(curIndex == 0)
{
curIndex = [mcpd currrentIndex];
location = (curIndex >= maxPoints ? curIndex - maxPoints + 2 : 0 );
}
}
//Animate based on the found location/max points
CPTPlotRange *newRange = [CPTPlotRange plotRangeWithLocation:CPTDecimalFromUnsignedInteger(location) length:CPTDecimalFromUnsignedInteger(maxPoints - 2)];
[CPTAnimation animate:plotSpace property:@"xRange" fromPlotRange:plotSpace.xRange toPlotRange:newRange duration:CPTFloat(0.01f) animationCurve:CPTAnimationCurveDefault delegate:nil];
NSLog(@"Old range:%@\nNew range:%@", plotSpace.xRange, newRange);
//Get the newest data
[self updateUIToPacketDataForDevice:[[Model instance] cur_deviceSpeakingWith]];
//Add it to the plot
for(NSString* keyID in plotData)
{
MCPlotDataObject* mcpd = (MCPlotDataObject*)[plotData objectForKey:keyID];
CPTPlot* linePlot = [graph plotWithIdentifier:keyID];
[linePlot insertDataAtIndex:[[mcpd points] count] - 1 numberOfRecords:1];
}
}
-(void)updateToNewPacketDataForDevice:(NSString *)deviceName
{
//Gets called from a different thread, so notify object it can update data and animations will happen on main thread.
newPacketReceived = YES;
}
-(void)updateUIToPacketDataForDevice:(NSString*)deviceName
{
//Get latest packet data
}
并且相应的对象保存在数据点和索引上,MCPlotDataObject.h
@interface MCPlotDataObject : NSObject
{
NSUInteger numPointsRemoved;
}
@property (readonly) NSUInteger currrentIndex;
@property (readonly) NSMutableArray* points;
@property (readonly) NSString* ID;
/**
This function adds a plot point for a plot line and then increments the currentIndex.
@param newPlotPoint The new NSNumber value for the line to plot and follow.
*/
-(void)addPlotPoint:(NSNumber*)newPlotPoint;
/**
This function attempts to remove a plot point. It will return YES if [points count] >= maxDataPoints.
@return NO if no points were removed
@return OR
@return YES if a plot point was removed.
*/
-(BOOL)removePlotPoint;
/**
This function returns the max data points value that each MCPDO has.
@return The maximum number of data points that are allowed in points array.
*/
+(NSUInteger)getMaxDataPoints;
@end
相应的.m
static const NSUInteger kMaxDataPoints = 102;
-(void)addPlotPoint:(NSNumber *)newPlotPoint
{
currrentIndex++;
[points addObject:newPlotPoint];
}
-(BOOL)removePlotPoint
{
if([points count] >= kMaxDataPoints)
{
[points removeObjectAtIndex:0];
numPointsRemoved++;
return YES;
}
else
return NO;
}
+(NSUInteger)getMaxDataPoints
{
return kMaxDataPoints;
}
有人能指出我为什么updateGraph()中的plotSpace没有被移动的正确方向吗?
答案 0 :(得分:1)
经过一天的调试后,我发现我的问题是动画持续时间。 1/100秒太快了。当我像Plot_Gallery示例项目中那样进入1/25秒时,图表会滚动。
编辑:
经过多一点调试后,60 fps似乎是最大值。