Je télécharge une image par cellule que je dois afficher dans un UITableView
en appelant une tâche de réseau asynchrone. Ceci est dans la classe UIViewController
de la table:Téléchargement et affichage des images dans la cellule appropriée
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard results.count > 0 else {
return UITableViewCell()
}
let myCell = tableView.dequeueReusableCell(withIdentifier: CustomCell.cellIdentifier, for: indexPath) as! CustomCell
let model = results[indexPath.row]
myCell.model = model
return myCell
}
Et voici CustomCell
:
class CustomCell: UITableViewCell {
// Several IBOutlets
static let cellIdentifier = "myCell"
let imageProvider = ImageProvider()
var model: MyModel? {
willSet {
activityIndicator.startAnimating()
configureImage(showImage: false, showActivity: true)
}
didSet {
guard let modelUrlStr = model?.imageUrlStr, let imageUrl = URL(string: modelUrlStr) else {
activityIndicator.stopAnimating()
configureImage(showImage: false, showActivity: false)
return
}
imageProvider.getImage(imageUrl: imageUrl, completion: {[weak self] (image, error) in
DispatchQueue.main.async {
guard error == nil else {
self?.activityIndicator.stopAnimating()
self?.configureImage(showImage: false, showActivity: false)
return
}
self?.imageView.image = image
self?.activityIndicator.stopAnimating()
self?.configureImage(showCoverImage: true, showActivity: false)
}
})
}
}
override func awakeFromNib() {
super.awakeFromNib()
configureImage(showCoverImage: false, showActivity: false)
}
override func prepareForReuse() {
super.prepareForReuse()
model = nil
}
private func configureImage(showImage: Bool, showActivity: Bool) {
// Update image view
}
}
Et ImageProvider
:
class ImageProvider {
var imageTask: URLSessionDownloadTask?
func getImage(imageUrl: URL, completion: @escaping DownloadResult) {
imageTask?.cancel()
imageTask = NetworkManager.sharedInstance.getImageInBackground(imageUrl: imageUrl, completion: { (image, error) -> Void in
if let error = error {
completion(nil, error)
} else if let image = image {
completion(image, nil)
} else {
completion(nil, nil)
}
})
}
}
Puisque les cellules peuvent être dynamiquement dequeued et réutilisés et le téléchargement est asynchrone et ensuite une image peut être réutilisée sur chaque cellule en faisant défiler, suis-je de cette façon en veillant à ce que chaque cellule montre toujours son im correspondant âge?
EDIT: Approche différente
Est-il approprié de conserver une référence au modèle dans la cellule? Penser à une architecture correcte MVC
. Qui devrait être responsable du téléchargement des images? La cellule (en lui passant uniquement l'URL au lieu de l'objet de modèle complet) ou le contrôleur de vue de la vue de la table (mise à jour de l'image dans la cellule dans sa méthode tableView:cellForRowAt:
)?
Merci. Je pense ... est-il correct d'avoir le modèle dans la cellule? Cela casse le modèle 'MVC', non? J'ai vu des exemples comme ça, en passant le modèle à la cellule et en gérant le téléchargement du réseau, mais serait-il plus approprié de déplacer cette logique vers le contrôleur de vue de la table? – AppsDev
@AppsDev Je vous conseille de faire la chose qui vous semble moins gênant. Si l'adhésion stricte à la structure architecturale vous fait ajouter de la complexité pour un avantage peu clair, je dirais qu'il faut l'éviter. Faire des choses comme dans votre exemple peut parfaitement se passer dans ce cas. Cela dépend de la structure de votre système. Suivez vos sentiments intestinaux :) – ivanmoskalev