2013-05-02 3 views
5

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

У меня есть rss-синтаксический анализатор, таблица с текстом, деталями и изображениями, установленными в AccessoryDisclosureIndicator.view.

Изображения загружаются с помощью простого GCD async: быстрая прокрутка для сотен результатов. NO lag, NO ошибки, если у меня хорошая связь.

Проблема в том, что на долю секунды - они мерцают при загрузке, потому что ячейка повторно используется. Кроме того, если соединение плохое, оно иногда оставляет изображение с откидным верхом, но текст/деталь верны, только изображение устарело ... Поэтому позвольте мне повторить, текст/подробности обновляются отлично и НИКОГДА не ошибается, просто изображение иногда редко попадает в очередь неправильно при плохих соединениях/прокручивании вперед и назад.

Мой вопрос, может кто-нибудь помочь мне кэшировать/пометить cell.accs.views? Я пробовал устанавливать cellIDs, но имел проблемы с моей реализацией. Мой ниже код работает отлично, если соединение никогда не замедляется, просто небольшое мерцание при повторной очереди на ячейку, которую я не против.

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

if (cell == nil) { 
    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; 

    [cell.textLabel setNumberOfLines:3]; 
    cell.textLabel.font = [UIFont boldSystemFontOfSize:14.0]; 

    [cell.detailTextLabel setNumberOfLines:3]; 
    cell.detailTextLabel.font = [UIFont systemFontOfSize:12.0]; 
    cell.detailTextLabel.textColor = [UIColor blackColor]; 
} 

RSSItem * rssItem = (RSSItem *)(_rssParser.rssItems)[indexPath.row]; 

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 
dispatch_async(queue, ^{ 
    //This is what you will load lazily 
    NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:rssItem.imageURL]]; 
    dispatch_sync(dispatch_get_main_queue(), ^{ 

     UIImageView *accImageView = [[UIImageView alloc] initWithImage:[UIImage imageWithData:data ]]; 

     [accImageView setFrame:CGRectMake(0, 0, accImageView.image.size.width, 92)]; 
     cell.accessoryView = accImageView; 

     //[cell setNeedsLayout];//not needed 
    }); 
}); 

[cell.textLabel setText:rssItem.title]; 
[cell.detailTextLabel setText:rssItem.summary]; 

return cell;} 

ответ

0

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

RSSItem * rssItem = (RSSItem *)(_rssParser.rssItems)[indexPath.row]; 

if (rssItem.imageURL == nil){ 
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)]; 
    [imageView setImageWithURL:[NSURL URLWithString:rssItem.imageURL] placeholderImage:[UIImage imageNamed:@"chevron.png"]]; 
    cell.accessoryView = imageView; 
}else{ 
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 110, 96)]; 
    imageView.contentMode = UIViewContentModeScaleAspectFit; 
    [imageView setImageWithURL:[NSURL URLWithString:rssItem.imageURL] placeholderImage:[UIImage imageNamed:@"loading.png"]]; 
    cell.accessoryView = imageView; 
} 

[cell.textLabel setText:rssItem.title]; 
[cell.detailTextLabel setText:rssItem.summary]; 

return cell; 
1

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

В конце концов, вы можете просто добавить:

cell.accessoryView = nil; 
+0

Моих проблемы, по-видимый глубже, чем это, спасибо Тхо – JoshP

1

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

Но это ваш ответ прямо там. Ячейка повторно используется, и я ничего не вижу в вашем коде, который удаляет изображение. Если изображение не является правильным изображением для этой строки (потому что оно повторно используется в другой строке), вам нужно установить cell.accessoryView на нуль (или на изображение с изображением заполнителя) сейчас. Правильное изображение не поступит до позже (dispatch_async).

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

http://www.apeth.com/iOSBook/ch37.html#_http_requests

прокрутки вниз, где он говорит: «Предположим, например, что вам нужно загружайте изображения в миниатюры в ячейках UITableView. Давайте рассмотрим, как эти изображения могут предоставляться лениво по требованию ».

+0

Да ... Я полагал, что нужно delagate кэша, чтобы предотвратить redownloading, но да ... я тоже нужен выяснить, как помечать клетки ... Я gunna читаю ваши смайлики, но да я noob – JoshP

+0

Фактический загружаемый рабочий пример проекта здесь: https://github.com/mattneub/Programming-iOS-Book-Examples/tree/master/ch37p920downloader/p754p772downloader – matt

+0

Ваш пример похож на что-то вроде как попробовать, спасибо. Хотите дать мне подсказку, как запустить rss.items.title/text/imgurl/array через addobject: d? – JoshP

10

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

Две вторичные вопросы:

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

    Вы можете сделать это путем проверки (на главной очереди) является ли даже видна клетка:

    dispatch_async(dispatch_get_main_queue(), ^{ 
        UITableViewCell *updateCell = [tableView cellForRowAtIndexPath:indexPath]; 
        if (updateCell != nil) { 
         // update image in `updateCell` 
        } 
    }); 
    

    Пожалуйста, обратите внимание, что это UITableView метод, cellForRowAtIndexPath, не следует путать с UITableViewDataSource методом, tableView:cellForRowAtIndexPath:. Первый проверяет, видима ли ячейка, и если да, возвращает указатель на нее. Последнее, очевидно, является методом, который вы ссылаетесь в своем вопросе, для создания и настройки ячеек таблицы.

    Также обратите внимание, что эта идея, что вы используете NSIndexPath, чтобы проверить, все ли видима ячейка, предполагает, что никакие дополнительные строки не могли быть вставлены в представление таблицы за прошедшее время. Это часто не является допустимым предположением, поэтому вам, возможно, придется вернуться к базовой модели и пересчитать indexPath для этой ячейки, прежде чем приступить к рассмотрению вышеприведенной логики, чтобы увидеть, все ли эта ячейка все еще видна или нет.

  2. Возможно, вы не захотите использовать глобальную очередь, потому что на очень медленной сети ваши запросы могут начать перезагружаться, и вы можете столкнуться с множеством параллельных сетевых запросов. Это проблема, потому что (а) система не может выполнять более 4 или 5 одновременных запросов одновременно; и (б) вы будете использовать ограниченное количество рабочих потоков, предоставляемых iOS. У вас действительно не должно быть более 4 или 5 одновременных запросов (если у вас есть более того, вы не только не увидите прироста производительности, но и фактически заблокируете и, в конечном итоге, тайм-аут). Я бы предложил вам использовать NSOperationQueue, для которого вы установили maxConcurrentOperationCount четыре или пять.

    Кроме того, это дает одну окончательную оптимизацию, в частности, делает эти подклассы NSOperation отменными (и, очевидно, делает логику отмены фактически отменяет сетевой запрос, содержащийся в нем). Таким образом, если вы быстро прокрутите до 100-й строки в таблице, вы действительно не хотите, чтобы видимые ячейки ожидали, что загрузка изображений для предыдущих 99 строк завершится первой. Если вы отмените запрос изображения для ячеек, которые прокручиваются с экрана, это устраняет эту проблему.

Если вы хотите, чтобы диагностировать поведение вашего приложения на самом деле медленное сетевое подключение, я хотел бы предложить, что вы получите Network Conditioner, который может иметь ваш Mac имитировать что-либо в диапазоне от полной скорости до действительно низкого качества Краю сеть на тренажере. В меню Xcode в Xcode выберите «Открыть инструмент разработчика ...», а затем выберите «Дополнительные инструменты для разработчиков». После входа в систему вы увидите опцию «Hardware IO Tools for Xcode». Средство Network Conditioner есть. Вы можете протестировать свое приложение в самых неблагоприятных сценариях, что может заставить вас задуматься не только о двух проблемах, описанных выше, но и о более сложных функциях, таких как кеширование изображений и т. П.

Вы действительно хотите кэшировать изображения, по крайней мере, на постоянное хранилище, в идеале как для ОЗУ, так и для постоянного хранения. Существуют категории UIImageView, которые затрагивают как асинхронные проблемы выше, так и предлагают некоторое кэширование: два, которые вы можете рассмотреть, - это SDWebImage и AFNetworking.

+0

Ohhh gawd, с включенным кондиционером линии. Я вижу, что ячейка будет циклически перемещаться по 4 или 5 изображениям, пока она не станет правильный ... Почему это так сложно? Я все для кеширования, но им не удалось добавить теги и проверить их, или я просто не могу найти хороший учебник ... – JoshP

+1

@JoshP Либо [SDWebImage] (https://github.com/rs/SDWebImage), либо [AFNetworking] (https://github.com/AFNetworking/AFNetworking) 'UIImageView', которые сделают вашу жизнь намного проще. Если вы действительно хотите написать свой собственный, дайте мне знать, и я могу помочь вам найти хорошие ответы. Когда я написал свой собственный, я лично использую комбинацию «NSCache» для кеша ОЗУ и папку «Документы» для постоянного кэша хранилища (хотя я думаю, что теперь соглашение будет использовать отдельную папку «Cache»). Я действительно предлагаю вам использовать AFNetworking или SDWebImage. – Rob

+0

Я попробую те, люди делают это каждый день, и я не могу найти хороший учебник, он просто умудряет ум, что таблица забывает о его прокрутке, правда? Как он может запомнить текст, но забыть изображение? Это похоже на то, что яблоко должно решать автоматически. Я собираюсь не деактивировать ячейки, но это смешно. Спасибо, на мой взгляд, я знаю, что это можно сделать, изменив мой код выше без каких-либо внешних классов и, возможно, только кеша приложений, но, как я уже сказал, мне нужно больше искать лучшие примеры. – JoshP

Смежные вопросы