Подтвердить что ты не робот

Прокрутка 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.

Приложение для проверки плавного прокрутки

4b9b3361

Ответ 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, и теперь прокрутка является гладкой.