I'm having a little trouble with my code, and I was hoping to get some help here
I have a uitableview, in each uitableviewcell I put an individual uislider. Each uislider, is used as a progress bar for music playing.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UIButton *button = nil;
UISlider *customSlider = nil;
static NSString *CellIdentifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIImage *image = [UIImage imageNamed:@"button_play.png"];
button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect frame = CGRectMake(340.0, 10.0, image.size.width, image.size.height);
button.frame = frame;
[button setBackgroundImage:image forState:UIControlStateNormal];
[button addTarget:self action:@selector(playAudio:) forControlEvents:UIControlEventTouchUpInside];
button.tag = 4;
[cell.contentView addSubview:button];
customSlider = [[UISlider alloc] initWithFrame:CGRectMake(10, 45, 456, 20)];
customSlider.minimumValue = 0.0;
customSlider.maximumValue = 100.0;
customSlider.continuous = YES;
customSlider.tag = 3;
customSlider.value = 0.0;
[cell.contentView addSubview:customSlider];
}
else {
customSlider = (UISlider *)[cell.contentView viewWithTag:3];
button = (UIButton *)[cell.contentView viewWithTag:4];
}
return cell;
}
- (void) playAudio:(UIButton *)sender {
UIButton *button = (UIButton *)[sender superview];
UITableViewCell *currentCellTouched = (UITableViewCell *)[button superview];
UITableView *currentTable = (UITableView *)[currentCellTouched superview];
NSIndexPath *indexPath = [currentTable indexPathForCell:currentCellTouched];
//currentCellPlaying type of UITableViewCell and accessible from the rest classe
currentCellPlaying = currentCellTouched;
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:NSLocalizedString([listFilesAudio objectAtIndex:indexPath.row], @"") ofType:@"mp3"]];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
player.delegate = self;
[player prepareToPlay];
[(UISlider *)[currentCellTouched.contentView viewWithTag:3] setMaximumValue:[player duration]];
[(UISlider *)[currentCellTouched.contentView viewWithTag:3] setValue:0.0];
NSTimer *sliderTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];
[player play];
}
- (void)updateTime:(NSTimer *)timer {
[(UISlider *)[currentCellPlaying.contentView viewWithTag:3] setValue:player.currentTime];
}
When I launch a single play, everything is okay and the progress bar is updated.
My problem is that when I scroll down/up the tableview, and the cell where the music is playing disappears, once we come back over the cell playing the music, the uislider is back to 0, and is not updated anymore... (a NSLOG confirms that we still go inside "updateTime" method)
If you have the solution for my problem, I would be very glad to read it.
Thank you in advance.
Otium's answer is not completely right, but it goes in the right direction. The celle are not all the same object, but (actually you implemented it that way) cells that leave visible area by scrolling are reused for display of others, scrolling into visible area. So when the cell playing the music becomes visible again, another slider object then the original one is displayed inside it. Moreover the currentCellPlaying
object (maybe) isn't shown anymore or is shown as another cell. So when you update it's viewWithTag:3
you (sometimes) won't see that.
What you should do, is storing a "indexCurrentPlaying" instead of cell or slider references. With that index you can decorate the cell at the end of cellForRowAt
.... And you can update the slider of exactly the cell at that Index inside updateTime
.
Hope that helps.
EDIT:
Some not-tested code that should work (maybe with some fixes, but it explains the idea, I think):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UIButton *button = nil;
UISlider *customSlider = nil;
static NSString *CellIdentifier = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
UIImage *image = [UIImage imageNamed:@"button_play.png"];
button = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect frame = CGRectMake(340.0, 10.0, image.size.width, image.size.height);
button.frame = frame;
[button setBackgroundImage:image forState:UIControlStateNormal];
[button addTarget:self action:@selector(playAudio:) forControlEvents:UIControlEventTouchUpInside];
button.tag = 4;
[cell.contentView addSubview:button];
customSlider = [[UISlider alloc] initWithFrame:CGRectMake(10, 45, 456, 20)];
customSlider.minimumValue = 0.0;
customSlider.maximumValue = 100.0;
customSlider.continuous = YES;
customSlider.tag = 3;
customSlider.value = 0.0;
[cell.contentView addSubview:customSlider];
}
else
{
customSlider = [cell viewWithTag:3];
}
if([indexPath isEqual:currentPlayingIndexPath])
{
currentPlayingSlider = customSlider;
[self updateTime];
}
else if(customSlider == currentPlayingSlider)
{
currentPlayingSlider = nil;
}
return cell;
}
- (void) playAudio
{
NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:NSLocalizedString([listFilesAudio objectAtIndex:currentPlayingIndexPath.row], @"") ofType:@"mp3"]];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
player.delegate = self;
[player prepareToPlay];
NSTimer *sliderTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(updateTime:) userInfo:nil repeats:YES];
[player play];
}
- (void)updateTime:(NSTimer *)timer
{
[currentPlayingSlider setValue:player.currentTime];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
currentPlayingIndexPath = indexPath;
currentPlayingSlider = [cellForRowAtIndexPath: currentPlayingIndexPath];
[self playAudio];
}
Be careful with NSIndexPath isEqual
. Seems to answer different in different versions of the SDK (look here) ... Since you only need the row it could be changed to currentPlayingIndexPath.row == indexPath.row
A UItableView uses dequeable cells, so all your cells are basically the same object. In the cellForRowAtIndexPath method you should set the progress of the slider again.