Как получить быстрый UICollectionView с большим количеством изображений (50-200)?

м используюUICollectionView в приложении, которое отображает довольно много фотографий (50-200), и яУ меня возникли проблемы с его быстрым подключением (например, с приложением Photoshop).

У меня есть обычайUICollectionViewCell сUIImageView как это'подвид. Я'м загрузки изображений из файловой системы, сUIImage.imageWithContentsOfFile:в UIImageViews внутри ячеек.

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

ПРИМЕЧАНИЕ: яя использую RubyMotion, поэтому яЯ напишу любые фрагменты кода в стиле Ruby.

Прежде всего, здесьмой пользовательский класс UICollectionViewCell для справки ...

class CustomCell < UICollectionViewCell
  def initWithFrame(rect)
    super

    @image_view = UIImageView.alloc.initWithFrame(self.bounds).tap do |iv|
      iv.contentMode = UIViewContentModeScaleAspectFill
      iv.clipsToBounds = true
      iv.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth
      self.contentView.addSubview(iv)
    end

    self
  end

  def prepareForReuse
    super
    @image_view.image = nil
  end

  def image=(image)
    @image_view.image = image
  end
end

Подход № 1

Сохраняя вещи простыми ..

def collectionView(collection_view, cellForItemAtIndexPath: index_path)
  cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
  image_path = @image_paths[index_path.row]
  cell.image = UIImage.imageWithContentsOfFile(image_path)
end

Прокрутка вверх / вниз - это ужасно. Это'с нервной и медленной

Подход № 2

Добавьте немного кеширования через NSCache ...

def viewDidLoad
  ...
  @images_cache = NSCache.alloc.init
  ...
end

def collectionView(collection_view, cellForItemAtIndexPath: index_path)
  cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
  image_path = @image_paths[index_path.row]

  if cached_image = @images_cache.objectForKey(image_path)
    cell.image = cached_image
  else
    image = UIImage.imageWithContentsOfFile(image_path)
    @images_cache.setObject(image, forKey: image_path)
    cell.image = image
  end
end

Снова, нервный и медленный на первой полной прокрутке, но с этого моментас плавным ходом.

Подход № 3

Загружайте изображения асинхронно ... (и сохраняйте кеширование)

def viewDidLoad
  ...
  @images_cache = NSCache.alloc.init
  @image_loading_queue = NSOperationQueue.alloc.init
  @image_loading_queue.maxConcurrentOperationCount = 3
  ...
end

def collectionView(collection_view, cellForItemAtIndexPath: index_path)
  cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
  image_path = @image_paths[index_path.row]

  if cached_image = @images_cache.objectForKey(image_path)
    cell.image = cached_image
  else
    @operation = NSBlockOperation.blockOperationWithBlock lambda {
      @image = UIImage.imageWithContentsOfFile(image_path)
      Dispatch::Queue.main.async do
        return unless collectionView.indexPathsForVisibleItems.containsObject(index_path)
        @images_cache.setObject(@image, forKey: image_path)
        cell = collectionView.cellForItemAtIndexPath(index_path)
        cell.image = @image
      end
    }
    @image_loading_queue.addOperation(@operation)
  end
end

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

мы также пытались заменить NSOperationDispatch::Queue.concurrent.async { ... } но это, кажется, то же самое.

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

Подход № 4

В отчаянии я решил предварительно загрузить все изображения как UIImages ...

def viewDidLoad
  ...
  @preloaded_images = @image_paths.map do |path|
    UIImage.imageWithContentsOfFile(path)
  end
  ...
end

def collectionView(collection_view, cellForItemAtIndexPath: index_path)
  cell = collection_view.dequeueReusableCellWithReuseIdentifier(CELL_IDENTIFIER, forIndexPath: index_path)
  cell.image = @preloaded_images[index_path.row]
end

Это оказалось немного медленным и нервным в первой полной прокрутке, но все хорошо после этого.

Резюме

Так что, если кто-нибудь может указать мне правильное направление, яБуду очень благодарен. Как приложение «Фото» делает этотак хорошо?

Примечание для кого-либо еще: [Принятый] ответ решил мою проблему с изображениями, появляющимися в неправильных ячейках, но я все еще получал прерывистую прокрутку даже после изменения размеров моих изображений до правильных размеров. Разобрав мой код до самого необходимого, я в конце концов обнаружил, что виновником является UIImage # imageWithContentsOfFile. Несмотря на то, что я предварительно нагревал кэш UIImages с помощью этого метода, UIImage, похоже, выполняет внутреннее ленивое кэширование. После обновления моего кода разогрева кеша используйте решение, описанное здесь -stackoverflow.com/a/10664707/71810 - У меня наконец-то была супер плавная прокрутка ~ 60FPS.

Ответы на вопрос(2)

Ваш ответ на вопрос