Индикатор выполнения ячейки дублируется на других ячейках

У меня есть проблема повторного использования клетки.

Все мои клетки идентичны, есть только один раздел. Ячейки включают метку, кнопку и индикатор выполнения.

Кнопка запускает звук (это простая кнопка воспроизведения). Когда вызывается метод делегата AVAudioPlayers, audioPlayerDidFinishPlaying:Я аннулирую NSTimer это обновляет индикатор выполнения, скрывает индикатор выполнения, а также обнуляет аудиоплеер.

Все это происходит в классе моей клетки, которая наследует от UITableViewCell,

Все работает как надо, кроме одного:

Прокрутка во время воспроизведения звука показывает тот же индикатор выполнения на новых ячейках.

Поэтому, если я коснусь, скажем, второй ячейки, вторая следующая ячейка, отображаемая при прокрутке, будет иметь индикатор выполнения, перемещающийся вперед (и остановка + скрытие, когда звук закончится).

Я покажу вам мой класс, полный класс, надеюсь, это поможет. Я действительно не знаю, как это исправить. Я попробовал if-заявления в cellForRow: но без какого-либо реального успеха.

@implementation SoundItemTableViewCell

- (void)awakeFromNib {
    // Initialization code
    self.progressBar.hidden = YES;
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];

    // Configure the view for the selected state
}

- (IBAction)playSound:(id)sender {

    [self.progressBar setProgress:0];
    self.progressBar.hidden = NO;

    NSString *filename = self.fileName;
    [self playSoundWithURL:[NSURL URLWithString:filename]];
}

- (void)playSoundWithURL:(NSURL*)url{


    NSError *error;
    if (self.audioPlayer){

        if(self.audioPlayer.playing){
            [self.audioPlayer stop];
        }
    }

    self.audioPlayer = [[AVAudioPlayer alloc]initWithContentsOfURL:url error:&error];
    self.audioPlayer.delegate = self;
    self.tmrProgress = [NSTimer scheduledTimerWithTimeInterval:0.1f target:self selector:@selector(updateElapsedTime) userInfo:nil repeats:YES];

    [self.audioPlayer prepareToPlay];
    [self.audioPlayer play];
    NSLog(@"Starting sound play with item : %@",url);

}

- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag{

    [self.progressBar setProgress:0 animated:YES];
    self.progressBar.hidden = YES;
    //self.progressBar = nil;
    [self.tmrProgress invalidate];
    self.tmrProgress = nil;
    player = nil;
    self.audioPlayer = nil;
    NSLog(@"Audio Finish");
}


- (void)updateElapsedTime{
    if (self.audioPlayer) {
        NSLog(@"Updating progress");
        [self.progressBar setProgress:[self.audioPlayer currentTime] / [self.audioPlayer duration]];
    }
}

Стол. pushList массив, содержащий URL-адреса файлов

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *identifier = @"SoundItemTableViewCell";
    SoundItemTableViewCell *cell = [self.tbPushList dequeueReusableCellWithIdentifier:identifier];

    if (cell == nil) {
        cell = [[SoundItemTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }
    cell.fileName    = [pushList objectAtIndex:indexPath.row];
    cell.lbName.text = [[[pushList objectAtIndex:indexPath.row] lastPathComponent] stringByDeletingPathExtension];
    return cell;

}

Лучшее, чего я мог добиться, - это скрыть индикатор выполнения, когда он выходит из поля зрения, и поэтому никакой другой индикатор выполнения также не показывался бы, но при повторной прокрутке к моей игровой ячейке будет воспроизводиться звук без индикатора выполнения, что является неудовлетворительным / плохим UX поведение.

3 ответа

Решение

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

Добавьте это свойство к вашему контроллеру представления...

@property (nonatomic) NSString *playingFilename;

Переехать - (IBAction)playSound:(id)sender в контроллере представления и установить строку при выборе определенной кнопки...

- (IBAction)playSound:(id)sender {

    UIButton *playButton = sender;
    NSInteger index = playButton.tag;

    SoundItemTableViewCell *cell = (SoundItemTableViewCell *)[self.tbPushList cellForRowAtIndexPath:[NSIndexPath indexPathForRow:index inSection:0]];

    [cell.progressBar setProgress:0];
    cell.progressBar.hidden = NO;

    self.playingFilename = [pushList objectAtIndex:index];

    // Personally I'd move the playing logic into the controller as well... spin up as many audio players as you need... but they shouldn't be in the cell.
    [self playSoundWithURL:[NSURL URLWithString:self.playingFileName]];
}

затем

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    static NSString *identifier = @"SoundItemTableViewCell";
    SoundItemTableViewCell *cell = [self.tbPushList dequeueReusableCellWithIdentifier:identifier];

    if (cell == nil) {
        cell = [[SoundItemTableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    }


   // tag your button.....
    cell.playButton.tag = indexPath.row;

    cell.lbName.text = [[[pushList objectAtIndex:indexPath.row] lastPathComponent] stringByDeletingPathExtension];

    // check to see if this index contains the right file
   BOOL isRightFile = ([self.playingFilename isEqualToString:[pushList objectAtIndex:indexPath.row]])

    cell.progressBar.hidden = !isRightFile;

    return cell;
}

Обязательно инициализируйте ВСЕ свойства вашей ячейки в cellForRowAtIndexPath:,
Поскольку ваши клетки перерабатываются, они сохранят свои существующие свойства.

В ячейке табличного представления для метода строки проверьте, выбрана ли строка или нет, а затем установите индикатор выполнения на ноль.

Другие вопросы по тегам