【发布时间】:2013-09-25 16:09:45
【问题描述】:
我希望能够在运行时更新/重新加载 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 平方图目前与我的需求无关,它只是一个参考。 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 中所假设的,如果我通过 NSTimer 从其他地方调用 reloadData,它会完全按照我的意愿执行。那为什么我不能从块内调用它呢?
【问题讨论】:
-
这与未出现的情节无关,但您应该直接返回
[_plotIndizesAndVolume objectAtIndex:index],而不是将其转换为浮点数并直接返回NSNumber。它将运行得更快,并使您的代码更易于阅读。 -
绘图空间的
xRange和yRange是否设置为使数据落在范围内?例如,对于 500 个数据点,xRange的位置应为 -10,长度为 100 以适合所有点。 -
@EricSkroch NSNumber->float->NSNumber 转换只是我试图找出代码处理数组对象的方式是否有错误(因为 objectAtIndex: 返回一个 id),我根本无法想象,但我想尝试每一种可能性,即使是那些我认为愚蠢的可能性。关于 xRange:这对于“展示”部分情节不应该很重要吗?我展示情节的哪些部分并不重要,根本没有任何变化。还是我的假设有误?
标签: ios objective-c core-plot novocaine