Я написал два способа асинхронной загрузки изображений внутри моей ячейки UITableView. В обоих случаях изображение будет загружаться нормально, но когда я прокручиваю таблицу, изображения будут меняться несколько раз, пока прокрутка не закончится и изображение не вернется к нужному изображению. Я понятия не имею, почему это происходит.
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
- (void)viewDidLoad
{
[super viewDidLoad];
dispatch_async(kBgQueue, ^{
NSData* data = [NSData dataWithContentsOfURL: [NSURL URLWithString:
@"http://myurl.com/getMovies.php"]];
[self performSelectorOnMainThread:@selector(fetchedData:)
withObject:data waitUntilDone:YES];
});
}
-(void)fetchedData:(NSData *)data
{
NSError* error;
myJson = [NSJSONSerialization
JSONObjectWithData:data
options:kNilOptions
error:&error];
[_myTableView reloadData];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
// Return the number of rows in the section.
// Usually the number of items in your array (the one that holds your list)
NSLog(@"myJson count: %d",[myJson count]);
return [myJson count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (cell == nil) {
cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
dispatch_async(kBgQueue, ^{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.poster.image = [UIImage imageWithData:imgData];
});
});
return cell;
}
... ...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
myCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
if (cell == nil) {
cell = [[myCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
}
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:@"http://myurl.com/%@.jpg",[[myJson objectAtIndex:indexPath.row] objectForKey:@"movieId"]]];
NSURLRequest* request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse * response,
NSData * data,
NSError * error) {
if (!error){
cell.poster.image = [UIImage imageWithData:data];
// do whatever you want with image
}
}];
return cell;
}
poster
? Предположительно, это изображение в его пользовательской ячейке, поэтому то, что делает EXEC_BAD_ACCESS, совершенно правильно. Вы правы, что вы не должны использовать ячейку в качестве хранилища для данных модели, но я не думаю, что это то, что он делает. Он просто дает пользовательской ячейке то, что ей нужно представить. Кроме того, и это более тонкая проблема, я бы с осторожностью относился к сохранению самого изображения в массиве вашей модели, поддерживающем ваше табличное представление. Лучше использовать механизм кэширования изображений, и объект вашей модели должен извлекаться из этого кэша.Ответы:
Предполагая, что вы ищете быстрое тактическое исправление, вам нужно убедиться, что изображение ячейки инициализировано, а также что строка ячейки все еще видна, например:
Приведенный выше код решает несколько проблем, связанных с тем, что ячейка используется повторно:
Вы не инициализируете изображение ячейки перед тем, как инициировать фоновый запрос (это означает, что последнее изображение для извлеченной из ячейки ячейки все еще будет видно, пока загружается новое изображение). Убедитесь, что
nil
вimage
свойствах любых изображений отображаются, иначе вы увидите мерцание изображений.Более тонкая проблема заключается в том, что в действительно медленной сети ваш асинхронный запрос может не завершиться, пока ячейка не прокрутится с экрана. Вы можете использовать
UITableView
методcellForRowAtIndexPath:
(не путать с одноименнымUITableViewDataSource
методомtableView:cellForRowAtIndexPath:
), чтобы увидеть, видна ли еще ячейка для этой строки. Этот метод вернется,nil
если ячейка не видна.Проблема заключается в том, что ячейка прокручивалась к тому времени, когда ваш асинхронный метод завершился, и, что еще хуже, ячейка была повторно использована для другой строки таблицы. Проверяя, видна ли строка, вы убедитесь, что случайно не обновили изображение изображением для строки, которая с тех пор прокручивалась за пределы экрана.
В некоторой степени не связанный с рассматриваемым вопросом, я все же был вынужден обновить его, чтобы использовать современные соглашения и API, в частности:
Используйте,
NSURLSession
а не отправлять-[NSData contentsOfURL:]
в фоновую очередь;Используйте
dequeueReusableCellWithIdentifier:forIndexPath:
вместоdequeueReusableCellWithIdentifier:
(но убедитесь, что для этого идентификатора используется прототип ячейки или класс регистра или NIB); иЯ использовал имя класса, которое соответствует соглашениям об именах Какао (то есть начинаются с заглавной буквы).
Даже с этими исправлениями есть проблемы:
Приведенный выше код не кэширует загруженные изображения. Это означает, что если вы прокрутите изображение за пределы экрана и вернетесь на экран, приложение может попытаться получить изображение снова. Возможно, вам повезет, что ваши заголовки ответа сервера позволят довольно прозрачное кэширование, предлагаемое
NSURLSession
иNSURLCache
, но если нет, то вы будете делать ненужные запросы к серверу и предлагая гораздо медленнее UX.Мы не отменяем запросы на ячейки, которые прокручиваются за пределы экрана. Таким образом, если вы быстро прокрутите до 100-й строки, изображение для этой строки может быть перенесено за запросами для предыдущих 99 строк, которые даже больше не видны. Вы всегда хотите убедиться, что вы расставляете приоритеты запросов на видимые ячейки для лучшего UX.
Простейшее решение этих проблем - использование
UIImageView
категории, например, предоставляемой SDWebImage или AFNetworking . Если вы хотите, вы можете написать свой собственный код для решения вышеуказанных проблем, но это много работы, и вышеупомянутыеUIImageView
категории уже сделали это для вас.источник
updateCell.poster.image = nil
tocell.poster.image = nil;
updateCell вызывается до того, как объявлено.AFNetworking
это определенно путь. Я знал об этом, но был ленив, чтобы использовать это. Я просто восхищаюсь тем, как кеширование работает с их простой строкой кода.[imageView setImageWithURL:<#(NSURL *)#> placeholderImage:<#(UIImage *)#>];
cellForRowAtIndexPath
приводит к мерцанию изображений при быстрой прокрутке», и я объяснил, почему это произошло, а также как это исправить. Но я продолжил объяснять, почему даже этого было недостаточно, описал несколько более глубоких проблем и спорил, почему вам лучше использовать одну из этих библиотек для более изящной обработки (расставьте приоритеты для запросов на видимые ячейки, кэшируйте, чтобы избежать избыточной сети). запросы и т. д.). Мне неясно, что еще вы ожидали в ответ на вопрос «как мне остановить мерцающие изображения в моем табличном представлении»./ * Я сделал это таким образом, а также проверил это * /
Шаг 1 = Зарегистрируйте пользовательский класс ячейки (в случае ячейки прототипа в таблице) или nib (в случае настраиваемого пера для настраиваемой ячейки) для таблицы, как это в методе viewDidLoad:
ИЛИ
Шаг 2 = Используйте метод UITableView «dequeueReusableCellWithIdentifier: forIndexPath:», как этот (для этого вы должны зарегистрировать класс или nib):
источник
Есть несколько платформ, которые решают эту проблему. Просто назвать несколько:
Swift:
Objective-C:
источник
SDWebImage
не решает эту проблему. Вы можете контролировать, когда изображение загружается, ноSDWebImage
назначить изображение,UIImageView
не спрашивая у вас разрешения на это. В принципе, проблема из вопроса до сих пор не решена с этой библиотекой.Свифт 3
Я пишу свою собственную легкую реализацию для загрузчика изображений с использованием NSCache. Нет мерцания изображения клетки!
ImageCacheLoader.swift
Пример использования
источник
Вот быстрая версия (с использованием @Nitesh Borad target C code): -
источник
Лучший ответ - неправильный способ сделать это :(. Вы на самом деле связали indexPath с моделью, что не всегда хорошо. Представьте, что некоторые строки были добавлены во время загрузки изображения. Теперь ячейка для данного indexPath существует на экране, но изображение уже не правильно! Ситуация довольно маловероятна и ее трудно воспроизвести, но это возможно.
Лучше использовать подход MVVM, связать ячейку с viewModel в контроллере и загрузить изображение в viewModel (назначение сигнала ReactiveCocoa методом switchToLatest), затем подписать этот сигнал и назначить изображение ячейке! ;)
Вы должны помнить, чтобы не злоупотреблять MVVM. Взгляды должны быть до смерти просты! Принимая во внимание, что ViewModels должен быть многоразовым! Вот почему очень важно связать View (UITableViewCell) и ViewModel в контроллере.
источник
UIImageView
совет по категориям, который я советую, то нет такой проблемы в отношении путей индекса.В моем случае это было не из-за кэширования изображений (используется SDWebImage). Это было из-за несоответствия тега пользовательской ячейки с indexPath.row.
На cellForRowAtIndexPath:
1) Назначьте значение индекса для вашей пользовательской ячейки. Например,
2) В главном потоке перед назначением изображения проверьте, принадлежит ли изображение соответствующей ячейке, сопоставив его с тегом.
источник
Спасибо "Роб" .... У меня была такая же проблема с UICollectionView, и ваш ответ помог мне решить мою проблему. Вот мой код:
источник
mycell *updateCell = (id)[collectionView cellForItemAtIndexPath:indexPath];
это никогда не ноль, так что это не имеет никакого эффекта.visibleCells
подобное, но я подозреваю, что использование[collectionView cellForItemAtIndexPath:indexPath]
более эффективно (и именно поэтому вы делаете этот вызов в первую очередь).updateCell
нетnil
, но затем не используете его. Вы должны использовать его не только для определения, является ли ячейка представления коллекции все еще видимой, но затем вы должны использоватьupdateCell
внутри этого блока, а неcell
(который может быть недействительным). И, очевидно, если это такnil
, вам не нужно ничего делать (потому что эта ячейка не видна).источник
Я думаю, что вы хотите ускорить загрузку вашей ячейки во время загрузки изображения для ячейки в фоновом режиме. Для этого мы сделали следующие шаги:
Проверка наличия файла в каталоге документов или нет.
Если нет, тогда загрузите изображение в первый раз и сохраните его в каталоге документов нашего телефона. Если вы не хотите сохранять изображение в телефоне, вы можете загружать изображения в ячейку прямо на фон.
Теперь процесс загрузки:
Просто включите:
#import "ManabImageOperations.h"
Код, как показано ниже для ячейки:
ManabImageOperations.h:
ManabImageOperations.m:
Пожалуйста, проверьте ответ и прокомментируйте, если возникает какая-либо проблема ....
источник
Просто поменяй,
В
источник
Вы можете просто передать свой URL,
источник
источник