Обновление TableView Async на iOS - PullRequest
2 голосов
/ 16 января 2012

У меня есть простой TableViewController, заполненный этим методом:

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

    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

        NSInteger row = [indexPath row];
        cell.textLabel.text = [[NSString alloc] initWithFormat:@"%d", row]; 

        dispatch_async(dispatch_get_main_queue(), ^{

            [cell setNeedsLayout];

        });

    }); 

    return cell;
}

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

Почему, прокрутка, номера не отсортированы?

enter image description here

Ответы [ 2 ]

3 голосов
/ 17 января 2012

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

Рассмотрите ваш код:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    static NSString *CellIdentifier = @"Cell";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }

    ...

    return cell;
}

Итак, вы создаете ячейку и возвращаете ее.Это может быть один из очереди повторного использования или новый.Все идет нормально.Но затем вы добавляете этот код:

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSInteger row = [indexPath row];
        cell.textLabel.text = [[NSString alloc] initWithFormat:@"%d", row]; 
        dispatch_async(dispatch_get_main_queue(), ^{
            [cell setNeedsLayout];
        });
    }); 

Итак, вы отправляетесь в фоновую очередь, чтобы установить что-то в этой ячейке.Но что произойдет, если произойдет такой ход событий:

  1. Таблица запрашивает ячейку.

  2. Новая ячейка создана, ячейка A, отправка получаетсделано, чтобы установить вещи позже.

  3. Пользовательские прокрутки и ячейка A выводятся из вида и помещаются в очередь повторного использования.

  4. Таблица запрашиваетячейка.

  5. Ячейка A возвращается из пула повторного использования, и очередная отправка помещается в очередь.

  6. Первая отправка заканчивается, установка параметров в ячейку A.

Это не то, что вам нужно, так как теперь ячейка A содержит старые данные.Конечно, если ваша очередь последовательная, вам, возможно, удастся с ней справиться, поскольку 2-я отправка всегда завершится после первой, но а) это нехорошо, поскольку устаревшие данные все равно будут видны, и б) ваша очередь может быть параллельной очередью.в любом случае.

Итак, какое правильное решение вы спросите?Ну, мой обычный подход к этому - иметь собственную ячейку.Похоже, вы хотите, чтобы ячейка загружала изображение, и я предполагаю, что вы хотите загрузить это изображение из Интернета?Таким образом, мой способ сделать это состоит в том, чтобы иметь пользовательскую ячейку с методом loadImage, который вызывается в каждой видимой ячейке, когда табличное представление либо перестало прокручиваться и не замедляется, либо табличное представление перестало замедляться.(См. UIScrollViewDelegate методы для того, что я имею в виду).

Затем в методе loadImage я запускаю HTTP-запрос, чтобы получить изображение, и жду его возвращения и устанавливаю UIImageView в моемПользовательская ячейка для изображения возвращается.Важным дополнительным битом является то, что у меня есть установщик в пользовательской ячейке для установки объекта / URL / того, что ячейка отображает, которая дает информацию о том, какое изображение загружать.Затем, когда ячейка получает запрос на новый URL, она отменяет старый и запускает новый при следующем вызове loadImage.

Надеюсь, это имеет смысл!Я на самом деле делаю это немного по-другому, но это настолько простое описание, которое я могу дать для общей идеи.К сожалению, у меня нет кода для показа, но, пожалуйста, задавайте любые вопросы, и я постараюсь добавить его еще.

3 голосов
/ 16 января 2012

UIKit не является поточно-ориентированнымВы не можете установить изображение UIImageView в другом потоке, таким же образом, вы не можете установить текст UILabel в другом потоке.Попробуйте вместо этого:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSInteger row = [indexPath row];
    [cell.textLabel  performSelectorOnMainThread:@selector(setText:) withObject:[NSString stringWithFormat:@"%d", row] waitUntilDone:YES];
}); 

Лично я не понимаю, почему вы вообще используете рассылку.Затраты на создание строки из целого числа минимальны, если за кулисами происходит больше обработки, чем показано здесь.

Вместо этого можно вместо этого поставить диспетчерский вызов:

 NSInteger row = [indexPath row];
 cell.textLabel.text = [NSString stringWithFormat:@"%i", row];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...