使 Completionhandler 在 For 循环中工作以获取图像

Posted

技术标签:

【中文标题】使 Completionhandler 在 For 循环中工作以获取图像【英文标题】:Make Completionhandler Work in For-Loop for Image Fetching 【发布时间】:2019-05-03 20:14:39 【问题描述】:

我正在从 Firebase 获取用户图片。一个用户最多可以在数据库中上传 6 张图像。我想以正确的顺序(0-6)将图像下载到 CollectionView 中。

当我尝试使用 CompletionHandler 时,它无法按我的意愿工作。 CollectionView 的重新加载在获取图像之前很久就完成了,没有显示图像,因为图像是在调用 reloadData 之后放入数组中的。

不胜感激有关此问题的帮助。

这是我的代码:

override func viewWillAppear(_ animated: Bool) 
        super.viewWillAppear(animated)
        downloadAndReload()
    

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int 
        return arrayImages.count
    

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell 

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "swipeCell", for: indexPath as IndexPath) as! ProfileCardCollectionViewCell
        let image = self.arrayImages[indexPath.item]
        cell.swipeProfileImage.image = image
        print("lägger ut cell")
        return cell
    

    func downloadAndReload()
        cachedImage(completion:  message in
            print(message)
            self.swipeCollectionView.reloadData()
        )
    


    func cachedImage(completion: @escaping (_ message: String) -> Void)
        if let uid = Auth.auth().currentUser?.uid
            for i in 0...6

                let url = URL(string: uid+String(i))

                SDWebImageManager.shared.loadImage(with: url, options: .refreshCached, progress: .none ,completed:  (image, data, error, _, _, _) in

                    if let error = error
                        print("Couldn't download image")
                        self.downloadImage(i: i, completion: _ in

                        )
                    
                    else
                        print("Could download image")
                        self.arrayImages.append(image!)
                    
                )
            
            completion("DONE")
        

    

    func downloadImage(i: Int, completion: @escaping (_ fetchWorked: String) -> Void)
        print("Downloading Image")

        let g = DispatchGroup()
        g.enter()
        if let uid = Auth.auth().currentUser?.uid
            let imagesStorageRef = Storage.storage().reference().child("profilepic/").child(uid+String(i))

            imagesStorageRef.downloadURL  url, error in
                guard let url = url else  return 

                SDWebImageManager.shared.loadImage(with: url, options: .refreshCached, progress: .none ,completed:  (image, data, error, _, _, _) in

                    if let error = error
                    
                    else
                        self.arrayImages.append(image!)
                    
                )

                g.leave()

            
            g.notify(queue: .main, execute: 
                completion("Done")
            )
        
    

【问题讨论】:

【参考方案1】:

问题是你有嵌套的闭包,所以当你调用时:

SDWebImageManager.shared.loadImage(with: url, options: .refreshCached, progress: .none ,completed:  (image, data, error, _, _, _) in

                if let error = error
                    print("Couldn't download image")
                    self.downloadImage(i: i, completion: _ in

                    )
                else
                    print("Could download image")
                    self.arrayImages.append(image!)
                
            )

其中的所有内容只有在“loadImage”完成时才会被调用,因此你的“for”将调用“SDWebImageManager.shared.loadImage”6次,然后调用“completion(“Done”)”。

因此您的代码将在处理完图像之前调用“completion("Done")”,因此在没有图像或可能有图像时重新加载数据。

在调用 reloadData() 之前,您需要等待获取完成,这取决于您的需要,但您可以根据需要获取图像,当您要将图像设置为单元格内容时,调用获取图像(首先从缓存或下载,如果不可用)然后只更新它们的显示位置。 这样,您的应用程序将在屏幕上更加动态地显示,同时设置占位符图像。

【讨论】:

谢谢,问题是 CollectionView 需要知道 arrayImages.count 有多大。我只希望上传的图像显示在单元格中。这就是为什么我想在重新加载之前先下载所有图像。 但是在您的代码中,它被硬编码为始终为 6,如果您知道它始终为 6,则它是一个固定值。请记住 Facebook 和 Instagram 的情况,它们不一定会显示所有图像,它们会按需加载,然后在加载图像时更新大小(单元格数量)。如果您想先下载它们,那么最好在呈现视图之前调用该函数,但这取决于您的应用程序的结构(

以上是关于使 Completionhandler 在 For 循环中工作以获取图像的主要内容,如果未能解决你的问题,请参考以下文章

在 Alamofire Swift 2.2 中使用 completionHandler

嵌入函数返回值后在函数中调用completionHandler?迅速

GMSGeoCoder reverseGeocodeCoordinate:completionHandler:在后台线程上

如何在swift中使用带有completionHandler的UIImageView load()函数

sendAsynchronousRequest:queue:completionHandler:应用程序在后台时失败

WKWebView的evaluateJavascript方法的completionHandler运行在啥线程上?