2016-03-12 25 views
6

Próbuję asynchronizować ładowanie obrazów w komórce FriendsTableView (UITableView). Obrazy ładują się dobrze, ale kiedy przewijam stół, obrazy zmieniają się kilka razy, a złe obrazy są przypisywane do niewłaściwych komórek.Swift Obrazy zmieniają się w niewłaściwe obrazy podczas przewijania po ładowaniu obrazu asynchronicznego do UITableViewCell

Próbowałem wszystkich metod, jakie można znaleźć w StackOverflow, w tym dodawanie znacznika do surowca, a następnie sprawdzanie go, ale to nie zadziałało. Sprawdzam również komórkę, która powinna zaktualizować się za pomocą indeksu IndexPath i sprawdź, czy komórka istnieje. Nie mam pojęcia, dlaczego tak się dzieje.

Oto mój kod:

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 
    } 

Odpowiedz

4

To dlatego UITableView ponowne wykorzystanie komórek. Załadowanie ich w ten sposób powoduje, że żądania asynchroniczne wracają w innym czasie i powodują zepsutą kolejność.

Proponuję użyć biblioteki, która ułatwiłaby ci życie jak Zimorodek. Będzie pobierał i buforował obrazy dla ciebie. Również nie będziesz musiał się martwić o połączenia asynchroniczne.

https://github.com/onevcat/Kingfisher

Kod z nią będzie wyglądać mniej więcej tak:

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

Próbowałem użyć biblioteki AlamofireImage, która również powinna to rozwiązać, ale nie mogłem jej uruchomić. Miało to taki sam wpływ na obrazy. Czy możesz wyjaśnić, z których metod powinienem korzystać z biblioteki Kingfisher? – BenNov

+0

Dodałem przykładowy kod do Twojego przypadku. To powinno działać dobrze. –

+0

w jaki sposób deklarujesz 'imageView'? – BenNov

10

Na cellForRowAtIndexPath:

1) przypisanie wartości indeksu do niestandardowego komórki. Na przykład,

cell.tag = indexPath.row 

2) na głównej nici, przed przypisaniem obrazu należy sprawdzić, czy obraz należy do odpowiedniej komórki, dopasowując je znacznika.

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

Zrobiłem to dla mnie, oprócz ustawienia widoku obrazu na zero w kodzie klasy komórki. – Supertecnoboff

+0

Ta metoda działa dobrze dla mnie. – zippo

+0

Proste i skuteczne! – iCediCe

2

UPDATE

Poniższy kod nie działa, ale później włączony do korzystania KingFisher który jest niezwykle prosty.

END UPDATE

nie chcą korzystać z biblioteki strony trzeciej, takich jak Alamofire i cell.tag nie działa dla mnie. Ustawienie początkowego obrazu na zero dało mi tę sztuczkę. Również zapisałem obrazy w pamięci podręcznej. Oto przedłużenie pisałem dla 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() 
    } 
} 

można wywołać tej metody, podobnie jak w cellForRowAt

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

Source1 Source2

0

Najlepszym rozwiązaniem tego problemu mam jest dla Swift 3 lub Swift 4 Po prostu napisz te dwie linie:

cell.videoImage.image = nil 

cell.thumbnailimage.setImageWith(imageurl!)