Reputation: 1609
I'm trying to retrieve over 1,000 images from the user's camera roll with PHAsset but it ends up crashing or taking a long time if it's just thumbnails. Here is my function where I retrieve the images...
func retrieveImages(thumbnail: Bool) {
/* Retrieve the items in order of modification date, ascending */
let options = PHFetchOptions()
options.sortDescriptors = [NSSortDescriptor(key: "modificationDate",
ascending: false)]
/* Then get an object of type PHFetchResult that will contain
all our image assets */
let assetResults = PHAsset.fetchAssetsWithMediaType(.Image,
options: options)
let imageManager = PHCachingImageManager()
assetResults.enumerateObjectsUsingBlock{(object: AnyObject!,
count: Int,
stop: UnsafeMutablePointer<ObjCBool>) in
if object is PHAsset{
let asset = object as! PHAsset
print("Inside If object is PHAsset, This is number 1")
var imageSize: CGSize!
if thumbnail == true {
imageSize = CGSize(width: 100, height: 100)
} else {
imageSize = CGSize(width: self.cameraView.bounds.width, height: self.cameraView.bounds.height)
}
/* For faster performance, and maybe degraded image */
let options = PHImageRequestOptions()
options.deliveryMode = .FastFormat
options.synchronous = true
imageManager.requestImageForAsset(asset,
targetSize: imageSize,
contentMode: .AspectFill,
options: options,
resultHandler: { (image, _: [NSObject : AnyObject]?) -> Void in
if thumbnail == true {
self.libraryImageThumbnails.append(image!)
self.collectionTable.reloadData()
} else {
self.libraryImages.append(image!)
self.collectionTable.reloadData()
}
})
/* The image is now available to us */
print("enum for image, This is number 2")
print("Inside If object is PHAsset, This is number 3")
}
print("Outside If object is PHAsset, This is number 4")
}
}
Please tell me if any more information is needed. Thank you!
Upvotes: 1
Views: 2294
Reputation: 126177
Generally you don't want to be loading tons of full-size or screen-sized images and keeping them in memory yourself. For that size, you're only going to be presenting one (or two or three, if you want to preload for screen-transition animations) at a time, so you don't need to fetch more than that at once.
Similarly, loading hundreds or thousands of thumbnails and caching them yourself will cause you memory issues. (Telling your collection view to reloadData
over and over again causes a lot of churn, too.) Instead, let the Photos framework manage them — it has features for making sure that thumbnails are generated only when needed and cached between uses, and even for helping you with tasks like grabbing only the thumbnails you need as the user scrolls in a collection view.
Apple's Using Photos Framework sample code project shows several best practices for fetching images. Here's a summary of some of the major points:
AssetGridViewController
, which manages a collection view of thumbnails, caches a PHFetchResult<PHAsset>
that's given to it upon initialization. Info about the fetch result (like its count) is all that's needed for the basic management of the collection view.
AssetGridViewController
requests thumbnail images from PHImageManager
individually, only when the collection view asks for a cell to display. This means that we only create a UIImage
when we know a cell is going to be seen, and we don't have to tell the collection view to reload everything over and over again.
On its own, step 2 would lead to slow scrolling, or rather, to slow catch-up of images loading into cells after you scroll. So, we also do:
Instead of using PHImageManager.default()
, we keep an instance of PHCachingImageManager
, and tell it what set of images we want to "preheat" the cache for as the user scrolls.
AssetGridViewController
has some extra logic for keeping track of the visible rect of the scroll view so that the image manager knows to prepare thumbnails for the currently visible items (plus a little bit of scroll-ahead), and can free resources for items no longer visible if it needs to. Note that prefetching doesn't create UIImage
s, just starts the necessary loading from disk or downloading from iCloud, so your memory usage stays small.
On selecting an item in the collection view (to segue to a full-screen presentation of that photo), AssetViewController
uses the default PHImageManager
to request a screen-sized (not full-sized) image.
Opportunistic delivery means that we'll get a lower-quality version (basically the same thumbnail that Photos has already cached for us from its previous use in the collection view) right away, and a higher quality version soon after (asynchronously, after which we automatically update the view).
If you need to support zooming (that sample code app doesn't, but it's likely a real app would), you could either request a full-size image right away after going to the full-screen view (at the cost of taking longer to transition from blown-up-thumbnail to fullscreen image), or start out by requesting a screen-sized image and then also requesting a full-sized image for later delivery.
Upvotes: 1
Reputation: 13
In CellForRowAtIndexPath use the PHImageManager's RequestImageForAsset method to lazy load the image property on your UIImageView.
Upvotes: 0