I'm looking to use a UISlider as an "Audio VU Meter" type indicator, where an audio track's level continuously updates the value of the UISlider. Each track in my application is currently situated as a UITableViewCell, with the VU Meter being one subview within the cell's content view. As many as 12 tracks are displayed at a time.
The updates to the VU meter are coming via a TCP/IP port, so once I receive the new VU level and set the fader value (mySlider.value = newValue
). However, the only way I can get the slider to update immediately is by calling performSelectorOnMainThread:@selector(reloadData) withObject:myTableView waitUntilDone:NO
. Seems like I shouldn't have to reload the entire table's data.
Code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
TrackTableCell *cell = (TrackTableCell *)[tableView dequeueReusableCellWithIdentifier:@"TrackTableCell"];
if(cell == nil)
{
[[NSBundle mainBundle] loadNibNamed:@"TrackTableCell" owner:self options:nil];
cell = [self trackTableCell];
/**************************/
/********* FADER *********/
/**************************/
VolumeSlider *fader = [[VolumeSlider alloc] initWithFrame:CGRectMake(0, 0, 265, 30)];
[fader setRotatedThumbImage:[UIImage imageNamed:@"FaderCap.png"]];
[fader setRotatedMinTrackImage:[UIImage imageNamed:@"VolumeSlider.png"]];
[fader setRotatedMaxTrackImage:[UIImage imageNamed:@"VolumeSlider.png"]];
[fader addTarget:self action:@selector(volumeFaderSliderAction:) forControlEvents:UIControlEventValueChanged];
fader.value = 0;
// rotate the slider
fader.transform = CGAffineTransformMakeRotation(-M_PI_2);
[cell.contentView addSubview:fader];
fader.bounds = CGRectMake(0, 0, 265, 30);
fader.center = CGPointMake(70, 515);
// property of the cell
cell.volumeSlider = fader;
}
// set the slider to 0
cell.volumeSlider.value = [[self.tracks objectAtIndex:indexPath.row] volume];
return cell;
}
The following method is called via a notification that the data model ([[tracks objectAtIndex:trackNumber] volume]
) has been updated.
- (void) updateVolumeFader:(NSNotification *)note
{
NSDictionary *extraInfo = [note userInfo];
int trackNumber = [[extraInfo objectForKey:@"trackNumber"] intValue];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:trackNumber inSection:0];
TrackTableCell *cell = (TrackTableCell *)[self.tracksTableView cellForRowAtIndexPath:indexPath];
cell.volumeSlider.value = [[tracks objectAtIndex:trackNumber] volume];
}
In reviewing the code, my guess is it's in the way I'm obtaining the cell pointer. I'm guessing calling cellForRowAtIndexPath
is not the best way. Further, I am just now realizing that calling cellForRowAtIndexPath
in itself should update the fader to the new value, in the tracks
array.
As has been suggested perhaps it's best to keep a cached array of all of my tracks' sliders (or all of my cells, for that matter). While this is certainly not the most efficient code, I still don't understand why it doesn't work?
If your notifications are not coming through on the main thread, your code to update the slider won't work. The code in your notification method is otherwise fine; simply place it inside a block and execute on the main thread, or register on the main thread (operation queue) when you register for the notification.