重新加载具有图像的 UICollectionViewCell
Reload UICollectionViewCell which has image
我正在尝试创建类似 Facebook NewsFeed 的内容,我正在使用自定义 UICollectionViewCell 来显示来自 JSON 的数据 (Text/Image)。我有 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)
//}
}
代码没有任何错误,但问题是图像仅在我单击它们时出现。 (我可以在一个单元格中看到 empty/white imageView,直到我点击它。当我点击 imageView 时,图像就会出现)我觉得它有点像 DispatchQueue.main.async 但是, 不确定。
我不想在获取图像后重新加载整个 collectionView。只想重新加载那些有图像的单元格。我发现
collectionView.reloadItemsAtIndexPaths(myArrayOfIndexPaths)
在很多解决方案上,但不知道如何让它在这种情况下工作。有人可以在这里帮助我吗?任何帮助将不胜感激。
你能试试这个吗?
DispatchQueue.main.async {
customCell.mainImage.image = image
}
而不是
customCell.mainImage.image = image
=====================更新======================
我最终不仅帮助 Snehal 解决了图像加载问题,还解决了集合视图和图像缓存方面的其他几个问题。我建议他使用像 SDWebImage 这样的第三方库,但在响应中发现他的 api returns 图像是 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]()
}
更好的方法是制作一个 class 模型,其中包含 url 图像和您的文本变量,例如
class任意名称{
var url: 字符串?
var desc: 字符串?
init(urlValue: String?, descValue:String? ){
url = urlValue
desc = descValue
}
}
现在在你的 viewcontroller 中创建以上 class 名称的数组
var 项目 = AnyName
现在,通过第一个 api 命中,您可以为该数组中的文本设置值并传递给 collectionview 并使用文本加载单元格,然后您可以更改 url 的数组值并轻松重新加载 collectionview
我正在尝试创建类似 Facebook NewsFeed 的内容,我正在使用自定义 UICollectionViewCell 来显示来自 JSON 的数据 (Text/Image)。我有 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)
//}
}
代码没有任何错误,但问题是图像仅在我单击它们时出现。 (我可以在一个单元格中看到 empty/white imageView,直到我点击它。当我点击 imageView 时,图像就会出现)我觉得它有点像 DispatchQueue.main.async 但是, 不确定。
我不想在获取图像后重新加载整个 collectionView。只想重新加载那些有图像的单元格。我发现
collectionView.reloadItemsAtIndexPaths(myArrayOfIndexPaths)
在很多解决方案上,但不知道如何让它在这种情况下工作。有人可以在这里帮助我吗?任何帮助将不胜感激。
你能试试这个吗?
DispatchQueue.main.async {
customCell.mainImage.image = image
}
而不是
customCell.mainImage.image = image
=====================更新======================
我最终不仅帮助 Snehal 解决了图像加载问题,还解决了集合视图和图像缓存方面的其他几个问题。我建议他使用像 SDWebImage 这样的第三方库,但在响应中发现他的 api returns 图像是 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]()
}
更好的方法是制作一个 class 模型,其中包含 url 图像和您的文本变量,例如 class任意名称{ var url: 字符串? var desc: 字符串?
init(urlValue: String?, descValue:String? ){
url = urlValue
desc = descValue
}
}
现在在你的 viewcontroller 中创建以上 class 名称的数组 var 项目 = AnyName 现在,通过第一个 api 命中,您可以为该数组中的文本设置值并传递给 collectionview 并使用文本加载单元格,然后您可以更改 url 的数组值并轻松重新加载 collectionview