重新加载UICollectionViewCell它有图像

问题描述:

我想创建类似Facebook NewsFeed的东西,其中,我使用自定义UICollectionViewCell显示来自JSON的数据(文本/图像)。我有2个不同的API。一个用于文本,另一个用于图像(每个单元格都没有图像)。重新加载UICollectionViewCell它有图像

所以,首先,我从我的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。只是想重新加载那些有Image的单元。我在许多解决方案上找到了 collectionView.reloadItemsAtIndexPaths(myArrayOfIndexPaths) ,但不知道如何在这种情况下使其工作。任何人都可以在这里帮我吗?任何帮助都感激不尽。

+0

[从这里开始引用。](https://stackoverflow.com/questions/28206492/refresh-certain-row-of-uitableview-based-on-int-in-swift )如果你仍然需要帮助,你可以问。 –

+0

谢谢!让我试试看。 – iUser

+0

你可以稍微解释一下 - > **问题是图片只有当我点击它们时才会出现** –

你可以试试吗?

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

代替

customCell.mainImage.image = image 

=====================更新============ ==========

我最终帮助Snehal不仅仅是图像加载问题,还有一些其他问题与集合视图和图像缓存。我建议他使用像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]() 
} 
+0

Omg! :|这是问题。啊..谢谢你,哥们,你的帮助! – iUser

+0

嘿HMHero,我再次得到同样的问题。我更新了我的代码。我正在使用缓存的图像来显示这个时间。这次图像不会显示,直到我滚动collectionView。一旦我滚动所有的图像都在正确的地方。 – iUser

+0

@Snehal可能是同一个问题。这个想法是,你必须确保你将图像设置为图像视图的地方,它必须在主线程中。 – HMHero

更好的办法做到这就像作出这样的类,它包含的图像和类似 类AnyName {VAR URL文本变量的网址模型:字符串? var desc:String?

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

} 

现在,在您的ViewController使上面的类名 VAR项目= AnyName 的阵列现在,通过第一API,打你能为这个数组中的文本设定值,并传递到的CollectionView和文字负荷细胞,然后你可以更改网址的阵列值并轻松地重新加载collectionview