2013-09-25 2 views
3

Я хочу иметь возможность обновлять/перезагружать ScatterPlot на устройствах iOS во время выполнения. Чтобы быть конкретным, я записываю аудиовход, делаю некоторые забавные вещи и ставил результат как простое значение NSNumber в массив. Всякий раз, когда это происходит, reloadData вызывается на графике, который я хочу обновить, но, к сожалению, ничего не происходит. В принципе, я делаю то же, что описано в ответе здесь: real time plotting on iPhone using core plot?. Это просто не работает, поэтому я предполагаю, что совершил какую-то глупую ошибку.Core-Plot: вызов reloadData на графике или графике изнутри блока не работает

Это соответствующий метод:

-(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; 
     } 
    } 
} 

Х Squared земля не имеет никакого отношения к моим потребностям на данный момент, это просто ссылка. LivePlot - это тот, который мне нужен и который нужно обновить. _plotIndizesAndVolume - это NSMutableArray, в котором хранятся мои значения. Он содержит 500 элементов, все из которых инициализируются с помощью [NSNumber numberWithFloat: 0.0f] в начале моего выполнения. Участок имеет также 500 индексов, поэтому мы не выходим за пределы или что-то еще.

Код исполняется отлично, метод вызывается, оператор printf работает и корректно отображает поплавки, план просто не обновляется.

Я также попытался

if (index < [_plotIndizesAndVolume count]) { 
    return [NSNumber numberWithFloat:[_plotIndizesAndVolume objectAtIndex:index]]; 
} else { 
    return [NSNumber numberWithFloat:0.0f]; 
} 

, не имея 500 0.0fs в массиве, так что я просто получить доступ к значениям по-разному от 0.0f. Это, конечно, лучше, это просто не работает.

Любые идеи?

Update 1:

  • Если я заполняю _plotIndizesAndVolume с просто случайными числами в начале моего заявления, сюжет совершенно следует за этими числами.

  • Если I printf в методе, я могу отлично прочитать обновленные значения после того, как на сюжет вызывается перезагрузка.

  • Однако эти обновленные значения не показаны на графике. Сюжет остается таким, как после вызова -reloadData на нем, независимо от того, какие значения были изменены в _plotIndizesAndVolume-Array.

Таким образом, от того, что я могу видеть: я могу получить доступ к массиву правильно, я могу обновить и прочитать новые значения правильно, сюжет по-прежнему остается неизменным и не меняется. Я смущен. Вероятно, я все-таки совершил какую-то случайную глупую ошибку, я просто этого не вижу.

Update2:

Ну, кажется, что моя проблема не сама reloadData, но откуда я это называю. Еще один контекст: я использую новокаин для измерения децибел через микрофон устройства. Это прекрасно работает. Затем я хочу визуализировать часть измеренного дБ на графике через corePlot (который я описал выше). Я получил метод под названием startMeasuring, который я называю somehen, а затем - очевидно - начните измерять децибел. Всякий раз, когда это происходит, я помещаю измеренное значение int _plotIndicesAndVolume и звоню reloadData на участке.

Метод выглядит следующим образом (и в значительной степени скопированы непосредственно из примеров новокаина):

-(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 правильно вызывается и значения корректно обновляются.

Update 3:

Если я позвоню reloadData где-то еще во время измерения, обновления графа просто отлично, и в соответствии с, что находится в _plotIndizesAndVolume. Он просто не обновляется, если я вызываю его изнутри блока (даже если я вызываю другой метод изнутри блока, который в свою очередь вызывает reloadData). Тем не менее, мне нужно обновление в реальном времени. Я предполагаю, что я мог бы перезагрузить сюжет один раз каждые x miliseconds (собираюсь попробовать это дальше), мне все еще интересно, почему это не работает в первую очередь.

Обновление 4: Как принято в обновлении 3, если я звоню reloadData откуда-то через NSTimer, он делает то, что я хочу, чтобы он делал. Почему я не могу назвать это изнутри блока?

+0

Это не связано с сюжетом не появляется, но вы должны вернуться '[_plotIndizesAndVolume objectAtIndex: индекс] 'непосредственно, вместо того, чтобы преобразовать его в float и обратно в' NSNumber'. Он будет работать быстрее и сделает ваш код более удобным для чтения. –

+0

Являются ли значения «xRange» и «yRange» для графика, чтобы данные попадали внутрь диапазонов? Например, с 500 точками данных, «xRange» должно иметь местоположение -10 и длину 100 для соответствия всем точкам. –

+0

@EricSkroch NSNumber-> float-> Преобразование NSNumber было только я пытаюсь выяснить, есть ли ошибка в том, как код обрабатывает объект массива (поскольку objectAtIndex: возвращает id), что я не мог себе представить все, но я хотел попробовать любую возможность, даже те, кого я считаю глупыми. О xRange: не должно ли это быть значительным для «показа» частей сюжета? Неважно, какие части сюжета я показываю, ничего не меняется. Или я ошибаюсь? – Aerius

ответ

3

Позвоните по номеру -reloadData (и любым другим методам Core Plot) из основного потока.

+0

Как я уже сказал в обновлении 4, это точно то, что я сделал. Почему это так? – Aerius

+0

Таймер запускает свой целевой метод в основном потоке. Если вы не можете гарантировать, что блок будет всегда выполняться в основном потоке, вы должны убедиться, что вызов 'reloadData' выполняется в основном потоке. Существует несколько способов сделать это, включая '-performSelectorOnMainThread: withObject: waitUntilDone:' и GCD. –

2

Возможно вам нужно обновить свой участок пространства х-диапазон/у-диапазон в вашем таймере

CPTXYPlotSpace *plotSpace = (CPTXYPlotSpace *)self.graph.defaultPlotSpace; 
[plotSpace scaleToFitPlots:[self.graph allPlots]]; 

CPTPlot *thePlot = [self.graph plotWithIdentifier:kIdentifier]; 
[thePlot reloadData];