Swift:异步加载和显示照片

4

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

如果有人的图库里有50张照片,一切都运作良好。但是当某人有数千张照片时,图库会加载10秒钟,这对我的应用程序不好。
当我从 Facebook 加载图像时也会出现同样的问题。应用程序等待直到下载完每张照片,然后才显示它们。我想在加载操作期间逐个显示图像,而不是等待所有图像都加载完成。

我知道我应该使用 DispatchQueue,我已经使用了,但是有一些我看不到的错误。
这是我用于从 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 类的问题也是一样的,所以非常感谢您的帮助。


到底是什么出了问题? - Ruslan Serebriakov
使用这段代码,应用程序会等待几秒钟,直到下载完所有图像,但我想在“下载”时逐个显示图像。当一张图片准备好后,就显示它,以此类推,直到所有图片都被显示。 - Matt199
@LeoDabus,是的,我实际上已经将其更改为.fastFormat,但它仍需要一些时间来加载。如果它可以逐个显示图像,那就太棒了。不是全部一起,而是在准备好时一个接一个地显示。 - Matt199
你需要使用fetchAssets逐个获取图片,而不是在循环中获取整个fetchAssets结果。 - Leo Dabus
这正是我所需要的!Leo,非常感谢。但是有一个问题,你在requestImageData中一次只获取一个资产,对吗? - Matt199
显示剩余4条评论
2个回答

6
你需要做的是在集合视图控制器中添加一个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扩展到异步请求图像数据,在完成时设置其图像。
extension UIImageView {    
    func fetchImage(asset: PHAsset, contentMode: PHImageContentMode, targetSize: CGSize, version:  PHImageRequestOptionsVersion = .current, deliveryMode: PHImageRequestOptionsDeliveryMode = .opportunistic) {
        let options = PHImageRequestOptions()
        options.version = version
        options.deliveryMode = deliveryMode
        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
            @unknown default: fatalError()
            }
            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: .aspectFill, targetSize: cell.imageView.frame.size * UIScreen.main.scale )
    return cell
}

别忘了在numberOfItemsInSection方法中返回fetchResult的数量。

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

class CollectionViewCell: UICollectionViewCell {
    @IBOutlet weak var imageView: UIImageView!
}

extension CGSize {
    static func *(lhs: CGSize, rhs: CGFloat) -> CGSize {
        .init(width: lhs.width * rhs, height: lhs.height * rhs)
    }
}

嗨,@LeoDabus 再次感谢您的帮助,但是在使用 spectFill 内容模式时,它给我提供了非常低分辨率的照片,而且我无法更改它。 - Matt199
我想在一行中显示3张照片,但是在滚动过程中,照片的分辨率会降低。很奇怪。 - Matt199

0

你有没有了解过SDWebImage?它内置了缓存支持,这样设备就不必每次都下载之前已经下载过的图片。而且它实现起来非常简单!


没有,但它也能与iPhone相册一起使用吗? - Matt199
使用SDWebImage的问题在于它无法获取已编辑的照片...也就是说,无论对图像或视频进行任何编辑都将无法获取...只能获取原始基本图像。因此,任何裁剪或特殊效果(例如深度效果)都将无法获取... - user13255207

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接