Swift:异步加载和显示照片(Swift: asynchronously loading and displaying photos)

我正在努力解决从iPhone到collectionView显示照片库的问题。

如果有人在画廊内有50张照片,一切正常。 问题是,当有人有数千张照片,然后画廊加载10秒钟,这对我的应用程序不利。 当我从Facebook加载图像时会出现同样的问题。 应用程序等待它下载每张照片然后显示。 我想在加载操作期间逐个显示图像,而不是等待它全部加载。

我知道我应该使用DispachQueue ,但我确实有一些我看不到的错误。 这是我用来从iPhone图库中获取图像的代码:

func grapPhotos() { let imgManager = PHImageManager.default() let requestOptions = PHImageRequestOptions() requestOptions.isSynchronous = true requestOptions.deliveryMode = .highQualityFormat let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] if let fetchResulat : PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) { if fetchResulat.count > 0 { for i in 0..<fetchResulat.count { imgManager.requestImage(for: fetchResulat.object(at: i), targetSize: CGSize(width: 200, height:200), contentMode: PHImageContentMode.aspectFill, options: requestOptions, resultHandler: { (image, eror) in self.imageArray.append(image!) DispatchQueue.main.async { self.collectionView.reloadData() } }) } } else { print("You have no photos") self.collectionView.reloadData() } } }

以及在collectionView显示它们的代码:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return imageArray.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "galleryCell", for: indexPath) as! GalleryCollectionViewCell let image = self.imageArray[indexPath.row] DispatchQueue.main.async { cell.gelleryImages.image = image } return cell }

可能同样的问题是我的Facebook课程,所以我将非常感谢你的帮助。

I'm struggling with the problem of displaying photo gallery from iPhone to collectionView.

Everything works fine if someone has 50 photos inside the gallery. The problem is when someone has thousands of photos, then the gallery is loading for 10 seconds, which is not good for my app. The same problem occurs when I'm loading images from Facebook. The app is waits until it downloads every photo then it displays. I'd like to display images one by one during the loading operation instead of waiting for it to load it all.

I know that I should use DispachQueue and I did but there is some bug that I don't see. Here is the code I use to fetch images from iPhone gallery:

func grapPhotos() { let imgManager = PHImageManager.default() let requestOptions = PHImageRequestOptions() requestOptions.isSynchronous = true requestOptions.deliveryMode = .highQualityFormat let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] if let fetchResulat : PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) { if fetchResulat.count > 0 { for i in 0..<fetchResulat.count { imgManager.requestImage(for: fetchResulat.object(at: i), targetSize: CGSize(width: 200, height:200), contentMode: PHImageContentMode.aspectFill, options: requestOptions, resultHandler: { (image, eror) in self.imageArray.append(image!) DispatchQueue.main.async { self.collectionView.reloadData() } }) } } else { print("You have no photos") self.collectionView.reloadData() } } }

And the code to display them in collectionView:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return imageArray.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "galleryCell", for: indexPath) as! GalleryCollectionViewCell let image = self.imageArray[indexPath.row] DispatchQueue.main.async { cell.gelleryImages.image = image } return cell }

Probably the same problem is with my Facebook class so I will be very grateful for your help.

最满意答案

您需要的是将fetchResult属性添加到集合视图控制器并在viewDidLoad方法中获取图像资源。

var fetchResult: PHFetchResult<PHAsset> = PHFetchResult() func fetchAssets() { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) } override func viewDidLoad() { super.viewDidLoad() fetchAssets() }

下一步是扩展UIImageView以请求图像数据在完成时异步设置其图像。

func fetchImage(asset: PHAsset, contentMode: PHImageContentMode, targetSize: CGSize) { let options = PHImageRequestOptions() options.version = .original PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { image, _ in guard let image = image else { return } switch contentMode { case .aspectFill: self.contentMode = .scaleAspectFill case .aspectFit: self.contentMode = .scaleAspectFit } self.image = image } }

现在,您可以在集合视图cellForItemAt方法中一次一个地获取图像:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell let asset = fetchResult.object(at: indexPath.row) cell.imageView.fetchImage(asset: asset, contentMode: .aspectFit, targetSize: cell.imageView.frame.size) return cell }

不要忘记返回numberOfItemsInSection方法的fetchResult计数。

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return fetchResult.count }

Sanple项目

What you need is to add a fetchResult property to your collection view controller and fetch your image Assets inside viewDidLoad method.

var fetchResult: PHFetchResult<PHAsset> = PHFetchResult() func fetchAssets() { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) } override func viewDidLoad() { super.viewDidLoad() fetchAssets() }

Next step is extend UIImageView to request the image data asynchronously setting its image on completion.

func fetchImage(asset: PHAsset, contentMode: PHImageContentMode, targetSize: CGSize) { let options = PHImageRequestOptions() options.version = .original PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { image, _ in guard let image = image else { return } switch contentMode { case .aspectFill: self.contentMode = .scaleAspectFill case .aspectFit: self.contentMode = .scaleAspectFit } self.image = image } }

Now you can fetch the images one at a time inside collection view cellForItemAt method:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell let asset = fetchResult.object(at: indexPath.row) cell.imageView.fetchImage(asset: asset, contentMode: .aspectFit, targetSize: cell.imageView.frame.size) return cell }

Don't forget to return the fetchResult count for numberOfItemsInSection method.

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return fetchResult.count }

Sanple project

Swift:异步加载和显示照片(Swift: asynchronously loading and displaying photos)

我正在努力解决从iPhone到collectionView显示照片库的问题。

如果有人在画廊内有50张照片,一切正常。 问题是,当有人有数千张照片,然后画廊加载10秒钟,这对我的应用程序不利。 当我从Facebook加载图像时会出现同样的问题。 应用程序等待它下载每张照片然后显示。 我想在加载操作期间逐个显示图像,而不是等待它全部加载。

我知道我应该使用DispachQueue ,但我确实有一些我看不到的错误。 这是我用来从iPhone图库中获取图像的代码:

func grapPhotos() { let imgManager = PHImageManager.default() let requestOptions = PHImageRequestOptions() requestOptions.isSynchronous = true requestOptions.deliveryMode = .highQualityFormat let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] if let fetchResulat : PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) { if fetchResulat.count > 0 { for i in 0..<fetchResulat.count { imgManager.requestImage(for: fetchResulat.object(at: i), targetSize: CGSize(width: 200, height:200), contentMode: PHImageContentMode.aspectFill, options: requestOptions, resultHandler: { (image, eror) in self.imageArray.append(image!) DispatchQueue.main.async { self.collectionView.reloadData() } }) } } else { print("You have no photos") self.collectionView.reloadData() } } }

以及在collectionView显示它们的代码:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return imageArray.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "galleryCell", for: indexPath) as! GalleryCollectionViewCell let image = self.imageArray[indexPath.row] DispatchQueue.main.async { cell.gelleryImages.image = image } return cell }

可能同样的问题是我的Facebook课程,所以我将非常感谢你的帮助。

I'm struggling with the problem of displaying photo gallery from iPhone to collectionView.

Everything works fine if someone has 50 photos inside the gallery. The problem is when someone has thousands of photos, then the gallery is loading for 10 seconds, which is not good for my app. The same problem occurs when I'm loading images from Facebook. The app is waits until it downloads every photo then it displays. I'd like to display images one by one during the loading operation instead of waiting for it to load it all.

I know that I should use DispachQueue and I did but there is some bug that I don't see. Here is the code I use to fetch images from iPhone gallery:

func grapPhotos() { let imgManager = PHImageManager.default() let requestOptions = PHImageRequestOptions() requestOptions.isSynchronous = true requestOptions.deliveryMode = .highQualityFormat let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] if let fetchResulat : PHFetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) { if fetchResulat.count > 0 { for i in 0..<fetchResulat.count { imgManager.requestImage(for: fetchResulat.object(at: i), targetSize: CGSize(width: 200, height:200), contentMode: PHImageContentMode.aspectFill, options: requestOptions, resultHandler: { (image, eror) in self.imageArray.append(image!) DispatchQueue.main.async { self.collectionView.reloadData() } }) } } else { print("You have no photos") self.collectionView.reloadData() } } }

And the code to display them in collectionView:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return imageArray.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "galleryCell", for: indexPath) as! GalleryCollectionViewCell let image = self.imageArray[indexPath.row] DispatchQueue.main.async { cell.gelleryImages.image = image } return cell }

Probably the same problem is with my Facebook class so I will be very grateful for your help.

最满意答案

您需要的是将fetchResult属性添加到集合视图控制器并在viewDidLoad方法中获取图像资源。

var fetchResult: PHFetchResult<PHAsset> = PHFetchResult() func fetchAssets() { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) } override func viewDidLoad() { super.viewDidLoad() fetchAssets() }

下一步是扩展UIImageView以请求图像数据在完成时异步设置其图像。

func fetchImage(asset: PHAsset, contentMode: PHImageContentMode, targetSize: CGSize) { let options = PHImageRequestOptions() options.version = .original PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { image, _ in guard let image = image else { return } switch contentMode { case .aspectFill: self.contentMode = .scaleAspectFill case .aspectFit: self.contentMode = .scaleAspectFit } self.image = image } }

现在,您可以在集合视图cellForItemAt方法中一次一个地获取图像:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell let asset = fetchResult.object(at: indexPath.row) cell.imageView.fetchImage(asset: asset, contentMode: .aspectFit, targetSize: cell.imageView.frame.size) return cell }

不要忘记返回numberOfItemsInSection方法的fetchResult计数。

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return fetchResult.count }

Sanple项目

What you need is to add a fetchResult property to your collection view controller and fetch your image Assets inside viewDidLoad method.

var fetchResult: PHFetchResult<PHAsset> = PHFetchResult() func fetchAssets() { let fetchOptions = PHFetchOptions() fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] fetchResult = PHAsset.fetchAssets(with: .image, options: fetchOptions) } override func viewDidLoad() { super.viewDidLoad() fetchAssets() }

Next step is extend UIImageView to request the image data asynchronously setting its image on completion.

func fetchImage(asset: PHAsset, contentMode: PHImageContentMode, targetSize: CGSize) { let options = PHImageRequestOptions() options.version = .original PHImageManager.default().requestImage(for: asset, targetSize: targetSize, contentMode: contentMode, options: options) { image, _ in guard let image = image else { return } switch contentMode { case .aspectFill: self.contentMode = .scaleAspectFill case .aspectFit: self.contentMode = .scaleAspectFit } self.image = image } }

Now you can fetch the images one at a time inside collection view cellForItemAt method:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell let asset = fetchResult.object(at: indexPath.row) cell.imageView.fetchImage(asset: asset, contentMode: .aspectFit, targetSize: cell.imageView.frame.size) return cell }

Don't forget to return the fetchResult count for numberOfItemsInSection method.

override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return fetchResult.count }

Sanple project