Прокрутка UITableView не является гладкой
У меня есть гладкая проблема прокрутки в моем UITableView с UITableViewCell, которая содержит UIImageView. Аналогичные проблемы можно найти по всему StrackOverflow, но ни одно из предлагаемых решений не помогло мне полностью избавиться от отставания.
Мой случай довольно распространен:
- изображения хранятся в хранилище приложений (в моем примере в комплекте приложений)
- изображения могут иметь разный размер (500x500, 1000x1000, 1500x1500).
- Мне нужно отобразить эти изображения в UITableView, где размер UIImageView равен 120x120 (сетчатка)
Я выполнил несколько советов по оптимизации и смог оптимизировать прокрутку.
К сожалению, он все еще не идеален. Это мой сценарий:
- сначала я переместил всю логику загрузки/обработки/изменения размера изображения в фоновый поток
- Повторное использование UITableViewCell включено
- Как только UITableViewCell находится в поле зрения, я очищаю старые значения (настройки до нуля) и запускаю фоновый поток для загрузки изображения.
- В этот момент мы находимся в фоновом потоке, и я добавляю задержку в 500 мс, чтобы часто не менять настройки нового изображения (в случае быстрого прокрутки) (см. ниже объяснение).
- если UIImage существует в кеше статического изображения (обычный словарь с экземплярами UIImage) - выберите его и перейдите к шагу 9.
- if not - загрузить новое изображение из пакета (imageWithName), используя URL-адрес приложения (в реальных сценариях изображения будут сохранены в хранилище приложений, а не в комплекте)
- После загрузки изображения изменение размера до 120x120 с использованием графического контекста
- сохранить измененное изображение в кеш статического изображения
- В этот момент мы имеем экземпляр для UIImage, и процесс находится в фоновом потоке. Отсюда мы возвращаемся к потоку пользовательского интерфейса с данным изображением
- если был очищен контекст данных (например, UITableViewCell исчез или повторно использовался для отображения другого изображения), мы пропускаем обработку текущего доступного изображения.
- если контекст данных одинаков - назначьте UIImage UIImageView с альфа-анимацией (UIView.Animate)
- как только UITableViewCell выходит из поля зрения - очистите контекст данных
Первоначально перед запуском нового фонового потока для получения изображения здесь (шаг 1) был проверен кеш UIImage без фонового потока. В этом случае, если у нас есть изображение в кеше, мы назначаем его мгновенно, и это приводит к большому отставанию во время быстрой прокрутки (мы часто добавляем изображения, пока мы получаем их мгновенно). Эти строки прокомментированы в моем примере, приведенном ниже.
Есть еще две проблемы:
- в какой-то момент во время прокрутки у меня все еще есть небольшое отставание (на
момент, когда я назначаю новый UIImage UIImageView.
- (это более заметно), когда вы нажимаете на элемент и возвращаетесь от деталей, есть задержка перед завершением анимации назад.
Любые предложения о том, как справляться с этими двумя проблемами или как оптимизировать мой сценарий, оцениваются
Пожалуйста, учтите, что образец написан на Xamarin, но я не считаю, что Xamarin является причиной проблемы, если у меня такая же проблема для приложения, написанного в ObjectiveC.
Приложение для проверки плавного прокрутки
Ответы
Ответ 1
Вы пытались заполнить свой TableView
только одним 120x120-изображением, которое сохраняется в вашем Bundle
? Таким образом, вы можете проверить, возникает ли проблема с вашим Image rendering
Вместо того, чтобы изменять размеры всех ваших изображений на 120x120 и сохранять их в кеше, я бы рекомендовал создать и использовать миниатюру всех ваших изображений. Вы как-то уже это делаете, но делаете это пару раз (каждый раз, когда вы прокручиваете или кеш заполнен).
В нашем последнем проекте мы имели UICollectionView
с обложками книг. Большинство обложек находились между 400-800 килобайтами и чувство, в то время как прокрутка была очень плохая. Таким образом, мы создали миниатюру для каждого изображения (уменьшилось около 40-50 килобайт) и использовали эскизы вместо реальных обложек. Работает как шарм! Я добавил функцию создания эскизов
- (BOOL) createThumbnailForImageAtFilePath:(NSString *)sourcePath withName:(NSString *)name {
UIImage* sourceImage = [UIImage imageWithContentsOfFile:sourcePath];
if (!sourceImage) {
//...
return NO;
}
CGSize thumbnailSize = CGSizeMake(128,198);
float imgAspectRatio = sourceImage.size.height / sourceImage.size.width;
float thumbnailAspectRatio = thumbnailSize.height/thumbnailSize.width;
CGSize scaledSize = thumbnailSize;
if(imgAspectRatio >= thumbnailAspectRatio){
//image is higher than thumbnail
scaledSize.width = scaledSize.height * thumbnailSize.width / thumbnailSize.height;
}
else{
//image is broader than thumbnail
scaledSize.height = scaledSize.width * imgAspectRatio;
}
UIGraphicsBeginImageContextWithOptions( scaledSize, NO, 0.0 );
CGRect scaledImageRect = CGRectMake( 0.0, 0.0, scaledSize.width, scaledSize.height );
[sourceImage drawInRect:scaledImageRect];
UIImage* destImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSString* thumbnailFilePath = [[self SOMEDIRECTORY] stringByAppendingPathComponent:name];
BOOL success = [UIImageJPEGRepresentation(destImage, 0.9) writeToFile:thumbnailFilePath atomically:NO];
return success;
}
Ответ 2
Попробуйте библиотеку Async Display facebook.
https://github.com/facebook/AsyncDisplayKit
Действительно прост в использовании.. от их руководства: http://asyncdisplaykit.org/guide/
_imageNode = [[ASImageNode alloc] init];
_imageNode.backgroundColor = [UIColor lightGrayColor];
_imageNode.image = [UIImage imageNamed:@"hello"];
_imageNode.frame = CGRectMake(10.0f, 10.0f, 40.0f, 40.0f);
[self.view addSubview:_imageNode.view];
Это декодирует изображение в фоновом потоке.
Я не уверен, легко ли использовать библиотеки iOS на Xamarin, но если это будет легко, сделайте это.
Ответ 3
Я подкласс Paul Hegarty CoreDataTableViewController и использую миниатюры моих фотографий в CoreDataTableView.
Ищите примеры в лекции 14 под названием FlickrFetcher и Photomania. Вам также потребуется загрузить CoreDataTableViewController по той же самой ссылке.
Создайте объект CoreData с соответствующим заголовком и определите любые атрибуты (переменные данных), которые вы хотите. Вам нужно будет определить два атрибута "Трансформируемый", один для фотографии и один для миниатюры.
Затем загрузите миниатюру в CoreDataTableView:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{ NSArray * exceptions = [NSArray arrayWithObjects: @ "SCR", @ "DNS", @ "NT", @ "ND", @ "NH", nil];
static NSString *CellIdentifier = @"resultsDisplayCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
MarksFromMeets *athleteMarks = [self.fetchedResultsController objectAtIndexPath:indexPath];
NSString* date = [ITrackHelperMethods dateToAbbreviatedString:athleteMarks.meetDate];
NSMutableString *title = [NSMutableString stringWithFormat:@"%@", athleteMarks.markInEvent];
NSMutableString *subTitle = [NSMutableString stringWithFormat:@"%@ - %@",date, athleteMarks.meetName];
[title replaceOccurrencesOfString:@"(null)"
withString:@""
options:0
range:NSMakeRange(0, [title length])];
// cell.imageView.image = athleteMarks.photoThumbNail; // Don't like image in front of record.
[cell.textLabel setFont:[UIFont
fontWithName:@"Helvetica Neue" size:18]];
[cell.detailTextLabel setFont:[UIFont fontWithName:@"Helvetica Neue" size:16]];
[cell.detailTextLabel setTextColor:[UIColor grayColor]];
// make selected items orange
if ([athleteMarks.eventPR integerValue] != 0
&& (![exceptions containsObject:athleteMarks.markInEvent])) {
title = [NSMutableString stringWithFormat:@"%@ (PR)",title];
[cell.textLabel setTextColor:[UIColor redColor]];
}
else if ([athleteMarks.eventSB integerValue] != 0
&& (![exceptions containsObject:athleteMarks.markInEvent])) {
title = [NSMutableString stringWithFormat:@"%@ (SB)",title];
[cell.textLabel setTextColor:[UIColor orangeColor]];
} else {
[cell.textLabel setTextColor:[UIColor grayColor]];
}
cell.textLabel.text = title;
cell.detailTextLabel.text = subTitle;
cell.indentationLevel = indentationLevelOne;
cell.indentationWidth = indentationForCell;
return cell;
}
Если вы хотите, я могу отправить вам пример категории для объекта NSManagedObject Sub-Class. Эта категория загружает фотографию и миниатюру в объект CoreData. Первый раз будет медленным. Однако после этого пользователь должен иметь возможность прокручивать TableView плавно, а затем все обновленные результаты будут загружаться автоматически. Дайте мне знать.
Хорошо, что CoreData обрабатывает все управление памятью.
Удачи!
Ответ 4
У меня недостаточно комментариев для комментариев. Итак, вот ответ, который помог мне прокрутить производительность таблицы:
- Сделать высоту таблицы больше, чем окно с возможностью просмотра. Ячейки будут загружаться "вне экрана" и улучшать гладкость прокрутки.
- Сделайте обработку изображений следующим способом:
-(void)tableView:(UITableView *)tableView
willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
Эти два трюка заставили мой стол течь очень приятно. Я получаю свои данные изображения из службы API, и AFNETWORKING имеет потрясающий загрузчик изображений, но не нужен для вас, поскольку изображения находятся в комплекте.
Ответ 5
Возможно, вы могли бы попробовать SDWebImage
. Это также xamarin component
который настраивает асинхронный загрузчик изображений и асинхронную память и кэширование образа диска с автоматическим управлением истечением кеша. Использование этого, вероятно, означало бы выбросить много сложного письменного кода, но, возможно, это стоит того, что ваш код станет намного проще. В iOS вы также можете настроить SDWebImageManager
внутри viewDidLoad контроллера:
- (void)viewDidLoad{
...
SDWebImageManager *manager = [SDWebImageManager sharedManager];
manager.delegate = self;
...
}
и установите контроллер представления в качестве делегата. Затем, когда вызывается следующий метод делегата:
- (UIImage *)imageManager:(SDWebImageManager *)imageManager transformDownloadedImage:(UIImage *)image withURL:(NSURL *)imageURL
вы можете масштабировать изображения до больших размеров до их кеширования.
Надеюсь, что это поможет.
Ответ 6
Weel У меня была аналогичная проблема, мой свиток не был гладким. Я вставляю в таблицу переменную UIImageView с внутренними labelViews.
Я сделал, чтобы изменить метод HeightforRowAtIndexPath для оценочногоHeightforRowAtIndexPath, и теперь прокрутка является гладкой.