ios - 重新加载具有图像的 UICollectionViewCell

标签 ios swift grand-central-dispatch uicollectionviewcell

我正在尝试创建类似 Facebook NewsFeed 的东西,我正在使用自定义 UICollectionViewCell 来显示来自 JSON 的数据(文本/图像)。我有 2 个不同的 API。一个用于文本,另一个用于图像(每个单元格都没有图像)。

因此,首先我从我的 textAPI 获取所有文本值到我的单元格中并重新加载 myCollectionView。这很完美。

现在对于图像,我正在使用 ImageFetcher 来获取图像,

func ImageFetcher(postId : NSNumber, completion : ((_ image: UIImage?) -> Void)!) {

    var image = UIImage()

    let urlString = "http://myImageAPI/Image/\(postId)"
    let jsonUrlString = URL(string: urlString)
    print(urlString)
    URLSession.shared.dataTask(with: jsonUrlString!) { (data, response, error) in

        do {
            if let jsonData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String:Any] {
                if let images = jsonData["Image"] as? String {
                    if images == "" {
                        print("No Image")
                    } else {
                        let dataDecoded : Data? = Data(base64Encoded: images, options: .ignoreUnknownCharacters)
                        image = UIImage(data: dataDecoded!)!
                        completion(image)
                    }
                }
            }
            else {
                completion(nil)
            }
        } catch {
            print(error.localizedDescription)
        }
    }.resume()

}

将图像显示到单元格中,

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

// Displaying other elements with text

DispatchQueue.main.async { 
        self.ImageFetcher(postId: self.myArray[indexPath.item].id!, completion: { (image) -> Void in
            customCell.mainImage.image = image
        })
// Declared "indexPaths" var indexPaths = [IndexPath]()
// Added this lines
      // let indexPath = IndexPath(item: indexPath.item, section: 0)
      // self.indexPaths.append(indexPath) 
    }

return customCell
}


override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    self.jsonParsing()

}
func jsonParsing() {
    //Fetching text data from textAPI

    //DispatchQueue.main.async {
    //      self.collectionView.reloadItems(at: self.indexPaths)
    //}
}

代码没有任何错误,但问题是图像仅在我单击它们时出现。 (我可以在一个单元格中看到空/白色的 imageView,直到我点击它。当我点击 imageView 时,图像就会出现)我觉得它与 DispatchQueue.main.async 有关系,但是,不确定。

我不想在获取图像后重新加载整个 collectionView。只想重新加载那些有图像的单元格。我发现 collectionView.reloadItemsAtIndexPaths(myArrayOfIndexPaths) 在很多解决方案上,但不知道如何让它在这种情况下工作。有人可以在这里帮助我吗?任何帮助将不胜感激。

最佳答案

你能试试这个吗?

DispatchQueue.main.async {
      customCell.mainImage.image = image
 }

代替

customCell.mainImage.image = image

=====================更新了====================

我最终不仅帮助 Snehal 解决了图像加载问题,还解决了 Collection View 和图像缓存方面的其他几个问题。我建议他使用像 SDWebImage 这样的第三方库,但发现他的 api 在响应中将图像作为 base64 字符串返回。所以我只是继续清理他的代码并编写我认为这是一个很好的字符串点可能对他有帮助的代码。

import UIKit 

class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 

    let imageView = UIImageView() 
    lazy var collectionView: UICollectionView = { 
        let layout = UICollectionViewFlowLayout() 
        layout.minimumInteritemSpacing = 10 
        layout.minimumLineSpacing = 10 
        layout.scrollDirection = .vertical 

        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) 
        collectionView.delegate = self 
        collectionView.dataSource = self 
        collectionView.register(CustomCell.self, forCellWithReuseIdentifier: NSStringFromClass(CustomCell.self)) 
        collectionView.backgroundColor = .clear 
        return collectionView 
    }() 


    override func viewDidLoad() { 
        super.viewDidLoad() 
        view.addSubview(collectionView) 
    } 

    override func viewWillLayoutSubviews() { 
         super.viewWillLayoutSubviews() 
         collectionView.frame = view.bounds 
    } 

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
        return 50 
    } 

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: NSStringFromClass(CustomCell.self), for: indexPath) as! CustomCell 
        cell.imageUrl = "http://myImageAPI/Image/\(postId)" 
        return cell 
    } 


    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 
         return CGSize(width: collectionView.frame.size.width - 2 * 20, height: 100) 
    } 

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { 

} 
} 


class CustomCell: UICollectionViewCell { 

    let imageView = UIImageView() 
    var imageUrl: String? { 
        didSet { 
            if let imageUrl = imageUrl, let url = URL(string: imageUrl) { 
                dataTask = imageView.loadImage(url: url) 
            } 

        } 
    } 

    var dataTask: URLSessionDataTask? 

    override init(frame: CGRect) { 
        super.init(frame: frame) 

        backgroundColor = .white 
        imageView.backgroundColor = UIColor.lightGray 
        imageView.contentMode = .scaleAspectFit 
        contentView.addSubview(imageView) 
    } 

    required init?(coder aDecoder: NSCoder) { 
        fatalError("init(coder:) has not been implemented") 
    } 

    override func prepareForReuse() { 
        super.prepareForReuse() 
        dataTask?.cancel() 
        imageView.image = nil 
    } 

    override func layoutSubviews() { 
        super.layoutSubviews() 
        imageView.frame = bounds 
    } 
} 


extension UIImageView { 
    @discardableResult func loadImage(url: URL) -> URLSessionDataTask? { 
        if let image = ImageLoadManager.manager.cachedImages[url.absoluteString] { 
            self.image = image 
            return nil 
        } 
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in 
            do { 
                guard let data = data else { 
                    return 
                } 
                if let jsonData = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String:Any] { 
                if let images = jsonData["PostImage"] as? String {// Pase the Base64 image string
                     if images == "" { 
                          print("No Image") 
                     } else { 
                         if let dataDecoded = Data(base64Encoded: images, options: .ignoreUnknownCharacters), let decodedImage = UIImage(data: dataDecoded) { 
                             DispatchQueue.main.async { 
                   ImageLoadManager.manager.cachedImages[url.absoluteString] = decodedImage 
                   self.image = decodedImage 
                              } 
                         } 
                     } 
                 } 
             } 
             else { 
                  DispatchQueue.main.async { 
                      self.image = nil 
                  } 
             } 
            } catch { 
                 print(error.localizedDescription) 
            } 
        } 
        task.resume() 
        return task 
    } 
} 

class ImageLoadManager { 
    static let manager = ImageLoadManager() 
    var cachedImages = [String: UIImage]() 
}

关于ios - 重新加载具有图像的 UICollectionViewCell,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45356157/

相关文章:

ios - 核心数据 Swift 3 关系

ios - 在 WKWebView 中禁用 JavaScript - 偏好设置不保存在 WKWebView 中

ios - 在另一个 ViewController 中显示和切换 ViewController

iOS 图表气泡图颜色

ios - 表格单元格 View 未右对齐

ios - 在单个 View 页面上的核心图中为 2 个不同的图创建两个 X 轴

ios - @sychronized 替代类方法

ios - Swift 无法将类型的值转换为 Realm 中的预期参数

iphone - 如何在使用SDWebimage显示之前先下载Cache中的网页图片?

ios - 为什么只有第一个任务退出后才调用dispatchGroup.notify?