Reload UICollectionViewCell which has image

前提是你 提交于 2019-12-24 18:53:27

问题


I am trying to create something like Facebook NewsFeed where, I am using custom UICollectionViewCell to display data (Text/Image) from JSON. I have 2 different APIs. One for text and another for images(Every cell doesn't have image).

So, First of all I am getting all text values in to my cells from my textAPI and reloading myCollectionView. That works perfect.

Now for the Images, I am using ImageFetcher to fetch images,

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()

}

To display image in to Cell,

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)
    //}
}

Code works without any error but the issue is Images only appears when I click on them. (I can see empty/white imageView in a cell until I click it. As soon as I click on imageView the Image appears) I feel its something with DispatchQueue.main.async but, not sure.

I do not want to reload the whole collectionView after fetching the images. Just want to reload those cells which have Image. I found collectionView.reloadItemsAtIndexPaths(myArrayOfIndexPaths) on many solution but don't know how to make it work in this scenario. Can anyone please help me here? Any help will be much appreciated.


回答1:


Can you try this?

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

instead of

customCell.mainImage.image = image

===================== Updated ======================

I ended up helping Snehal not only image load problem but also a couple of other issues with collection view and image caches. I suggested him to use a third party library like SDWebImage but found out his api returns image as base64 string in the response. So I just go ahead and clean up his code and write the code that I think it's a good string point that might help him.

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]() 
}



回答2:


Better way to do this is like make a model that class that contains url of images and your text variables like class AnyName { var url: String? var desc: String?

init(urlValue: String?,  descValue:String? ){
    url = urlValue
    desc = descValue
}

}

Now in your viewcontroller make array of above class name var items = AnyName Now by first api hitting you can set values for text in this array and passed to collectionview and load cells with text and thereafter you can change arrayvalues for url and reload collectionview easily



来源:https://stackoverflow.com/questions/45356157/reload-uicollectionviewcell-which-has-image

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!