我希望能够在运行时更新/重新加载 iOS 设备上的 ScatterPlot。具体来说,我记录音频输入,做一些有趣的事情,并将结果作为一个简单的 NSNumber 值放入一个数组中。每当发生这种情况时,都会在我要更新的 Plot 上调用 reloadData,但遗憾的是,完全没有任何反应。基本上,我做的和这里的答案中描述的一样:real time plotting on iPhone using core plot? . 它只是不起作用,所以我认为我犯了一些愚蠢的错误。
这是相关的方法:
-(NSNumber *) numberForPlot:(CPTPlot *)plot field:(NSUInteger)fieldEnum recordIndex:(NSUInteger)index {
double val = (index/5.0) -10;
if (fieldEnum == CPTScatterPlotFieldX) {
return [NSNumber numberWithDouble:val];
} else {
if ([plot.identifier isEqual: @"X Squared Plot"]) {
return [NSNumber numberWithDouble:10-(0.25*val*val)];
} else if( [plot.identifier isEqual:@"LivePlot"]) {
if (!_isMeasuring) {
return [NSNumber numberWithFloat:0.0f];
} else {
printf("%f", [[_plotIndizesAndVolume objectAtIndex:index] floatValue]);
return [NSNumber numberWithFloat:[[_plotIndizesAndVolume objectAtIndex:index] floatValue]];
}
} else {
return 0;
}
}
}
X Squared Plot 目前与我的需求无关,它只是一个参考。LivePlot 是我需要并想要更新的。_plotIndizesAndVolume 是我存储我的值的 NSMutableArray。它有 500 个元素,所有这些元素在我执行开始时都用 [NSNumber numberWithFloat:0.0f] 初始化。Plot 也有 500 个 indizes,因此我们不会越界或其他任何事情。
代码执行良好,方法被调用,printf 语句工作并正确显示浮点数,Plot 只是没有更新。
我也试过
if (index < [_plotIndizesAndVolume count]) {
return [NSNumber numberWithFloat:[_plotIndizesAndVolume objectAtIndex:index]];
} else {
return [NSNumber numberWithFloat:0.0f];
}
数组中没有 500 个 0.0fs,所以我只是访问与 0.0f 不同的值。这当然更好,它也不起作用。
有任何想法吗?
更新1:
如果我在应用程序开始时仅用随机数填充 _plotIndizesAndVolume,则绘图完全遵循这些数字。
如果我在方法中使用 printf,一旦在绘图上调用了 reload,我就可以完美地读取更新的值。
但是,这些更新的值不会显示在图中。无论 _plotIndizesAndVolume-Array 中的哪些值发生变化,绘图都保持在对其调用 -reloadData 后的状态。
因此,据我所见:我可以正确访问数组,可以正确更新和读取新值,绘图仍然保持不变并且不会改变。我很困惑。可能我仍然犯了一些随机的愚蠢错误,我只是没有看到它。
更新2:
好吧,似乎我的问题不是 reloadData 本身,而是我从哪里调用它。更多背景信息:我正在使用 novocaine 通过设备麦克风测量分贝。这工作正常。然后,我想通过 corePlot(我在上面描述过)在图表中可视化部分测量的 dB。我得到了一种方法startMeasuring
,我有时会调用它,然后 - 显然 - 开始测量分贝。每当发生这种情况时,我都会将测量值 int_plotIndicesAndVolume
并调用reloadData
绘图。
该方法看起来像这样(并且大部分是直接从 novocain 示例中复制的):
-(void) startMeasuring {
if (_isMeasuring) {
return;
}
__weak ViewController * wself = self;
self.ringBuffer = new RingBuffer(32768, 2);
self.audioManager = [Novocaine audioManager];
// MEASURE SOME DECIBELS!
// ==================================================
__block float dbVal = 0.0;
__block int count = 0;
__block int limiter = 0;
[self.audioManager setInputBlock:^(float *data, UInt32 numFrames, UInt32 numChannels) {
vDSP_vsq(data, 1, data, 1, numFrames*numChannels);
float meanVal = 0.0;
vDSP_meanv(data, 1, &meanVal, numFrames*numChannels);
float one = 1.0;
vDSP_vdbcon(&meanVal, 1, &one, &meanVal, 1, 1, 0);
dbVal = dbVal + 0.2*(meanVal - dbVal);
float reducedVal = (dbVal + 66.0f)/3;
if (!wself.measuringPaused && limiter > 10) {
[wself.plotIndizesAndVolume replaceObjectAtIndex:count withObject:[NSNumber numberWithFloat:reducedVal]];
[[wself.graph plotWithIdentifier:@"LivePlot"] reloadData];
limiter = -1;
++count;
}
++limiter;
}];
[self.audioManager play];
_isMeasuring = true;
}
我reloadData
从测量块内调用。我不太了解使用块,我只是假设我可以做到这一点。但是,如果我尝试更改数组中的数据,然后从其他地方手动重新加载绘图,它会起作用并且绘图会相应地更新。问题是否与我在块内调用方法有关?
即使这样,我仍然不确定为什么它不起作用,因为 printf 语句可以完美运行 - 所以我假设 updateData 方法被正确调用并且值被正确更新。
更新 3:
如果我reloadData
在测量过程中从其他地方打电话,图表会更新得很好,并且会根据_plotIndizesAndVolume
. 如果我从块内调用它,它根本不会更新(即使我从块内调用另一个方法,而该方法又调用reloadData
)。但是,我需要实时更新。我想我也可以每 x 毫秒重新加载一次绘图(接下来会尝试),我仍然很好奇为什么它一开始就不起作用。
更新 4:正如更新 3 中所假设的,如果我reloadData
通过 NSTimer 从其他地方调用,它会完全按照我的意愿执行。那为什么我不能从块内调用它呢?