Ich benutze UICollectionView
in einer App, die ziemlich viele Fotos (50-200) anzeigt und ich habe Probleme, es bissig zu bekommen (so schnell wie Fotos App zum Beispiel).Wie bekommt man eine bissige UICollectionView mit vielen Bildern (50-200)?
Ich habe eine benutzerdefinierte UICollectionViewCell
mit einer UIImageView
als Subview. Ich lade Bilder aus dem Dateisystem mit UIImage.imageWithContentsOfFile:
in die UIImageViews innerhalb der Zellen.
Ich habe jetzt einige Ansätze versucht, aber sie waren entweder fehlerhaft oder hatten Leistungsprobleme.
HINWEIS: Ich benutze RubyMotion, damit ich alle Code-Schnipsel im Ruby-Stil schreiben kann.
Zunächst einmal ist hier meine individuelle UICollectionViewCell Klasse als Referenz ...
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
Ansatz # 1
Dinge halten einfach ..
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
Bildlauf nach oben/Es ist schrecklich, dies zu benutzen. Es ist nervös und langsam.
Ansatz # 2
ein bisschen Caching über nscache Hinzufügen ...
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
wieder sprunghaft und langsam auf das erste volle Scroll aber es ist glatt Segeln von da.
Ansatz # 3
laden die Bilder asynchron ... (und hält das Caching)
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
Das sah vielversprechend aus, aber es ist ein Fehler, dass ich nach unten nicht verfolgen kann. In der ersten Ansicht laden alle Bilder das gleiche Bild und wenn Sie ganze Blöcke scrollen, laden Sie ein anderes Bild, aber alle haben dieses Bild. Ich habe versucht, es zu debuggen, aber ich kann es nicht herausfinden.
Ich habe auch versucht, die NSOperation mit Dispatch::Queue.concurrent.async { ... }
zu ersetzen, aber das scheint das gleiche zu sein.
Ich denke, das ist wahrscheinlich der richtige Ansatz, wenn ich es richtig funktionieren könnte.
Ansatz # 4
In Frustration habe ich beschlossen, nur Vorbelastung alle Bilder als 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
Dies erwies ein bisschen langsam und ruckartig in die erste sein aus volle Schriftrolle, aber danach alles gut.
Zusammenfassung
Also, wenn jemand mich in die richtige Richtung zeigen kann ich Ihnen sehr dankbar, in der Tat sein würde. Wie macht Fotos App es so gut?
Hinweis für alle anderen: [Die akzeptierte] Antwort aufgelöst meine Ausgabe von Bildern in den falschen Zellen erscheinen, aber ich war immer noch abgehackt Scrollen immer auch nach meinen Bildern Ändern der Größe Größen zu korrigieren. Nachdem ich meinen Code auf das Wesentliche reduziert hatte, fand ich schließlich, dass UIImage # imageWithContentsOfFile der Schuldige war. Obwohl ich mit dieser Methode einen Cache von UIImages vorerwärmte, scheint UIImage intern eine Art faules Caching durchzuführen. Nach dem Aktualisieren meines Cache-Erwärmungscodes, um die hier beschriebene Lösung zu verwenden - stackoverflow.com/a/10664707/71810 - hatte ich endlich super weiches ~ 60FPS Scrollen.
Ja, danke! Ich kann nicht glauben, dass ich das nicht gesehen habe.Die Performance stottert im Moment noch auf Scroll, aber ich glaube, dass das nur deshalb der Fall ist, weil meine Bilder zu groß für die Zellen sind. – alistairholt
Hinweis für alle anderen: Diese Antwort löste das Problem, dass Bilder in den falschen Zellen angezeigt werden, aber ich bekam immer noch abgehacktes Scrollen, auch nachdem ich meine Bilder auf die richtigen Größen skaliert hatte. Nachdem ich meinen Code auf das Wesentliche reduziert hatte, fand ich schließlich, dass UIImage # imageWithContentsOfFile der Schuldige war. Obwohl ich mit dieser Methode einen Cache von UIImages vorerwärmte, scheint UIImage intern eine Art faules Caching durchzuführen. Nachdem ich meinen Cache-Erwärmungscode aktualisiert hatte, um die hier beschriebene Lösung zu verwenden - http://StackOverflow.com/a/10664707/71810 - hatte ich endlich super glatte ~ 60FPS Scrolling. – alistairholt