2016-03-12 31 views
6

Sto provando a caricare immagini asincrone all'interno della mia cella FriendsTableView (UITableView). Le immagini si caricano bene ma quando farò scorrere la tabella le immagini cambieranno alcune volte e le immagini sbagliate verranno assegnate alle celle sbagliate.Swift Le immagini si trasformano in immagini errate durante lo scorrimento dopo il caricamento asincrono dell'immagine su UITableViewCell

Ho provato tutti i metodi che ho trovato in StackOverflow incluso l'aggiunta di un tag al raw e quindi il controllo ma non ha funzionato. Sto anche verificando la cella che dovrebbe aggiornarsi con indexPath e controllare se la cella esiste. Quindi non ho idea del perché questo sta accadendo.

Ecco il mio codice:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCellWithIdentifier("friendCell", forIndexPath: indexPath) as! FriendTableViewCell 
     var avatar_url: NSURL 
     let friend = sortedFriends[indexPath.row] 

     //Style the cell image to be round 
     cell.friendAvatar.layer.cornerRadius = 36 
     cell.friendAvatar.layer.masksToBounds = true 

     //Load friend photo asyncronisly 
     avatar_url = NSURL(string: String(friend["friend_photo_url"]))! 
     if avatar_url != "" { 
       getDataFromUrl(avatar_url) { (data, response, error) in 
        dispatch_async(dispatch_get_main_queue()) {() -> Void in 
         guard let data = data where error == nil else { return } 
         let thisCell = tableView.cellForRowAtIndexPath(indexPath) 
         if (thisCell) != nil { 
          let updateCell = thisCell as! FriendTableViewCell 
          updateCell.friendAvatar.image = UIImage(data: data) 
         } 
        } 
       } 
     } 
     cell.friendNameLabel.text = friend["friend_name"].string 
     cell.friendHealthPoints.text = String(friend["friend_health_points"]) 
     return cell 
    } 

risposta

4

Questo perché UITableView riutilizza le cellule. Caricandoli in questo modo, le richieste asincrone si ripresentano in tempi diversi e rovinano l'ordine.

Suggerisco di utilizzare una libreria che renderebbe la vita più facile come Kingfisher. Scaricherà e memorizzerà nella cache le immagini per te. Inoltre non dovresti preoccuparti delle chiamate asincrone.

https://github.com/onevcat/Kingfisher

il codice con sarebbe simile a questa:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
     let cell = tableView.dequeueReusableCellWithIdentifier("friendCell", forIndexPath: indexPath) as! FriendTableViewCell 
     var avatar_url: NSURL 
     let friend = sortedFriends[indexPath.row] 

     //Style the cell image to be round 
     cell.friendAvatar.layer.cornerRadius = 36 
     cell.friendAvatar.layer.masksToBounds = true 

     //Load friend photo asyncronisly 
     avatar_url = NSURL(string: String(friend["friend_photo_url"]))! 
     if avatar_url != "" { 
      cell.friendAvatar.kf_setImageWithURL(avatar_url) 
     } 
     cell.friendNameLabel.text = friend["friend_name"].string 
     cell.friendHealthPoints.text = String(friend["friend_health_points"]) 
     return cell 
    } 
+0

Ho provato ad usare la libreria AlamofireImage che avrebbe dovuto risolvere anche questo, ma non riuscivo a farlo funzionare, aveva lo stesso effetto sulle immagini. Ti spiego spiegando quali metodi dovrei usare della libreria Kingfisher? – BenNov

+0

Ho aggiunto un codice di esempio per il tuo caso. Questo dovrebbe funzionare bene. –

+0

come stai dichiarando 'imageView'? – BenNov

10

Su cellForRowAtIndexPath:

1) Assegnare un valore di indice per il tuo cellulare personalizzato. Ad esempio,

cell.tag = indexPath.row 

2) Il filo conduttore, prima di assegnare l'immagine, controllare se l'immagine appartiene la cella corrispondente facendo corrispondere con l'etichetta.

dispatch_async(dispatch_get_main_queue(), ^{ 
    if(cell.tag == indexPath.row) { 
    UIImage *tmpImage = [[UIImage alloc] initWithData:imgData]; 
    thumbnailImageView.image = tmpImage; 
    }}); 
}); 
+0

Questo ha fatto il trucco per me, oltre a impostare la visualizzazione dell'immagine su zero nel codice di classe della cella. – Supertecnoboff

+0

Questo metodo funziona bene per me. – zippo

+0

Semplice ed efficace! – iCediCe

2

UPDATE

Il seguente codice ha funzionato, ma poi sono passato ad usare KingFisher che è molto semplice.

FINE UPDATE

Non ho voluto usare una libreria di terze parti come Alamofire e cell.tag non ha funzionato per me. Impostare l'immagine iniziale su zero ha fatto il trucco per me. Ho anche memorizzato le immagini nella cache. Qui è l'estensione che ho scritto per UIImageView:

let imageCache = NSCache<NSString, UIImage>() 

extension UIImageView { 

    func downloadImage(from imgURL: String!) { 
     let url = URLRequest(url: URL(string: imgURL)!) 

     // set initial image to nil so it doesn't use the image from a reused cell 
     image = nil 

     // check if the image is already in the cache 
     if let imageToCache = imageCache.object(forKey: imgURL! as NSString) { 
      self.image = imageToCache 
      return 
     } 

     // download the image asynchronously 
     let task = URLSession.shared.dataTask(with: url) { (data, response, error) in 
      if error != nil { 
       print(error!) 
       return 
      } 

      DispatchQueue.main.async { 
       // create UIImage 
       let imageToCache = UIImage(data: data!) 
       // add image to cache 
       imageCache.setObject(imageToCache!, forKey: imgURL! as NSString) 
       self.image = imageToCache 
      } 
     } 
     task.resume() 
    } 
} 

È possibile chiamare questo metodo in questo modo in cellForRowAt

cell.postImage.downloadImage(from: self.posts[indexPath.row].pathToImage) 

Source1 Source2

0

la migliore soluzione per questo problema che ho è per Swift 3 o Swift 4 Basta scrivere queste due righe

cell.videoImage.image = nil 

cell.thumbnailimage.setImageWith(imageurl!)