2013-11-18 2 views
1

У меня есть UICollectionView, который показывает некоторые данные из объекта CoreData. Этот объект имеет множество атрибутов, а один из них - image (трансформативный тип).Как работать с большими изображениями в iOS?

My UICollectionView прокрутка не является гладкой. Я думаю, что причина - большие картинки в image атрибут (от 300 до 500k).

Вот код моего cellForItemAtIndexPath:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    AppDelegate *d = (AppDelegate *)[[UIApplication sharedApplication] delegate]; 
    static NSString *CellIdentifier = @"cellRecipe"; 

    RecipeCell *cell = (RecipeCell *)[self.collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath]; 

    cell.image.layer.borderColor = [UIColor blackColor].CGColor; 
    cell.image.layer.borderWidth = 1.0f; 

    Recipes *recipe = [recipesArray_ objectAtIndex:indexPath.item]; 

    if ([recipe.isPaid integerValue] == 0) 
     [cell.freeImage setHidden:NO]; 
    else [cell.freeImage setHidden:YES]; 

    NSObject *object = [[NSUserDefaults standardUserDefaults] objectForKey:@"com.________.fullhomechef"]; 
    if ([object isEqual:[NSNumber numberWithBool:YES]]) { 
     [cell.freeImage setHidden:YES]; 
    } 

    if ([recipe.isFavorite integerValue] == 0) 
     [cell.favImage setImage:[UIImage imageNamed:@"toFavorite.png"]]; 
    else [cell.favImage setImage: [UIImage imageNamed:@"toFavorite_.png"]]; 

    if ([recipe.isFitness integerValue] == 1) 
     [cell.fitImage setHidden:NO]; 
    else [cell.fitImage setHidden:YES]; 

    if ([d.currLang isEqualToString:@"ru"]) 
     [cell.recipeName setText: recipe.nameru]; 
    else [cell.recipeName setText: recipe.nameen]; 

    [cell.image setImage:recipe.image]; 

    int difficulty = [recipe.difficulty intValue]; 
    [cell.difficultyImage setImage: [mainTabController imageForRating:difficulty]]; 

    return cell; 
} 

Я попытался прокомментировать линию [cell.image setImage:recipe.image]; но прокрутка еще
прерывистая.

Что я не так? Может быть, есть какой-то метод работы с большими изображениями? Cuz в предыдущих версиях я использовал изображения размером 50-70k.

P.S. Если я терпеливо прокручу его до конца, прокрутка станет более гладкой.

После ответа Гуто Araujo

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"cellRecipe"; 

    RecipeCell *cell = (RecipeCell *)[self.collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath]; 

    cell.image.layer.borderColor = [UIColor blackColor].CGColor; 
    cell.image.layer.borderWidth = 1.0f; 

    Recipes *recipe = [fetchRecipes objectAtIndexPath:indexPath]; 

    if ([recipe.isPaid integerValue] == 0) { 
     [cell.freeImage setHidden:NO]; 
    } 
    else [cell.freeImage setHidden:YES]; 

    if (wasPaid == YES) { 
     [cell.freeImage setHidden:YES]; 
    } 

    if ([recipe.isFavorite integerValue] == 0) 
     [cell.favImage setImage:[UIImage imageNamed:@"toFavorite.png"] ]; 
    else [cell.favImage setImage: [UIImage imageNamed:@"toFavorite_.png"]]; 

    if ([recipe.isFitness integerValue] == 1) 
     [cell.fitImage setHidden:NO]; 
    else [cell.fitImage setHidden:YES]; 

    if ([d.currLang isEqualToString:@"ru"]) { 
     [cell.recipeName setText: recipe.nameru]; 
    } else [cell.recipeName setText:recipe.nameen]; 

    __weak typeof(self) weakSelf = self; 
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 
     UIImage *image = recipe.image; 

     dispatch_async(dispatch_get_main_queue(), ^{ 
      // Set via the main queue if the cell is still visible 
      if ([weakSelf.collectionView.indexPathsForVisibleItems containsObject:indexPath]) { 
       RecipeCell *cell = 
       (RecipeCell *)[weakSelf.collectionView cellForItemAtIndexPath:indexPath]; 
       cell.image.image = image; 
      } 
     }); 
    }]; 
    [thumbnailQueue addOperation:operation]; 

    int difficulty = [recipe.difficulty intValue]; 
    [cell.difficultyImage setImage: [mainTabController imageForRating:difficulty]]; 

    return cell; 
} 
+1

Почему каждый раз приходится устанавливать новый 'MainTabController'? –

+0

@MarceloFabri это простая функция, которая возвращает небольшое изображение в зависимости от ** уровня сложности ** от 1 до 5, и я уже инициализировал его в ** viewDidLoad ** – Romowski

+0

Убедитесь, что размер изображения такой же, как ваша ячейка коллекция вид. Если размер изображения больше размера ячейки коллекции, тогда прокрутка не будет гладкой. – Manthan

ответ

3

Один из подходов будет использовать очередь операций для загрузки изображений в фоновом режиме, как описано в этом UICollectionView tutorial by Bryan Hansen, в частности, шаги # 45-47:

Добавить свойство для операции очереди:

@property (nonatomic, strong) NSOperationQueue *thumbnailQueue; 

Initialize и Настройка maxConcurrentOperationCount в viewDidLoad:

self.thumbnailQueue = [[NSOperationQueue alloc] init]; 
self.thumbnailQueue.maxConcurrentOperationCount = 3; // Try different values for this variable 

Используйте операцию очереди в collectionView:cellForItemAtIndexPath: для загрузки изображений из основных данных асинхронно:

// Change [cell.image setImage:recipe.image] for the code below 

__weak typeof(self) weakSelf = self; 
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ 
    UIImage *image = recipe.image; 

    dispatch_async(dispatch_get_main_queue(), ^{ 
     // Set cell.image via the main queue if the cell is still visible 
     if ([weakSelf.collectionView.indexPathsForVisibleItems containsObject:indexPath]) { 
      RecipeCell *cell = 
       (RecipeCell *)[weakSelf.collectionView cellForItemAtIndexPath:indexPath]; 
      cell.image = image; 
     } 
    }); 
}]; 

[self.thumbnailQueue addOperation:operation]; 

Update 1:

Кроме того, вы можете сделать несколько вещи для оптимизации загрузки изображения:

  1. Создайте миниатюрный атрибут в объекте Рецепт, чтобы сохранить меньшую версию изображения, которое будет использоваться в ячейках коллекции. Сделайте это изображение не большим, чем максимальная ширина и высота RecipeCell. Затем, используя этот атрибут в приведенном выше блоке:

    UIImage *image = recipe.thumbnail; 
    
  2. использовать внешнее хранилище для основных изображений. Я хотел бы предложить вам попробовать вариант «Хранить в Внешняя запись файла» из атрибута изображения:

    Store In External Record File

    Использование этой опции позволит Основные данные управления внешнего хранения файлов для вас. Кроме того, вы можете сохранить только путь к изображению и сохранить файлы на диске, как указано в @HAS.Вот вопрос, обсуждать эти варианты: Storing UIImage in Core Data with the new External Storage flag

  3. Если вы используете «Store В Внешнем Record File» варианта, рассмотреть вопрос о переносе основного изображения на отдельное Фото Entity с отношением к Рецепту: Рецепт < -> Фото. В этом случае атрибут image будет находиться в объекте Photo. Это так, что они не загружаются в память до тех пор, пока не будут соблюдены отношения.

    Существует дополнительное обсуждение лучших способов хранения изображений в Core Data здесь: How to store an image in core data

    Update 2:

  4. Если скроллинг еще не является гладким, рассмотреть вопрос об использовании NSCache как предложено @DavidH , Ниже приведен пример реализации: Caching an Image and UICollectionView. На самом деле, кажется правильным, даже если прокрутка уже работает плавно.

Надеюсь, это поможет.

+0

Спасибо, но что-то не так в строке '__weak typeof (self) * weakSelf = self;' – Romowski

+0

А, я исправил его в редактировании. В этом случае нет необходимости в '*'. –

+0

((к сожалению .. – Romowski

1

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

Во-первых, если вы можете предварительно запрограммировать первый набор изображений, необходимых для коллекции, поэтому у вас есть их при первом появлении первого представления.

Как только вы их получите, вы можете попытаться начать получать больше в фоновом режиме или, при желании, после того, как представление коллекции завершит отображение первого набора (вам нужно поэкспериментировать здесь).

Идея состоит в том, что, как только у вас есть что показать пользователю, вы пытаетесь предварительно извлечь «трудно получить» предметы немедленно и кешировать их, надеясь, что сможете получить их быстрее, чем пользователь может прокручивать.

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

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

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

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

+0

Итак, вы сохранили бы изображения в базе данных? Я все еще думаю, что это лучше сохранить URL-адреса (см. [эту ссылку] (https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/CoreData/Articles/cdPerformance.html), в конце она говорит: «Это лучше, однако, если вы можете хранить BLOB как ресурсы в файловой системе и поддерживать ссылки (например, URL-адреса или пути) к этим ресурсам. Затем вы можете загрузить BLOB по мере необходимости. «Как вы думаете? Это самый эффективный (и наиболее эффективный) способ обработки изображений? – HAS

+1

@HAS Извините, что пропустил это. Что я сделал, это сохранить только небольшие миниатюры в репозитории, для большей проблемы s они были сохранены на диске, и в репозитории только был файл FilePath для изображения. Описанный мной метод одинаковый для обоих методов. Кроме того, я применял строгий порядок, поэтому изображения ниже в таблицеView не показывались до тех пор, пока предыдущий не был. Это выглядело очень красиво и закончено, но было много работы, чтобы получить право (и я мог бы не делать этого так снова!) –

+0

Спасибо за объяснение David :) – HAS

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