2016-03-12 120 views
6

我試圖異步加載我的FriendsTableView(UITableView)單元格內的圖片。圖像加載正常,但是當我滾動表格時,圖像會改變幾次,錯誤的圖像被分配到錯誤的單元格。異步圖像加載到UITableViewCell後滾動異常圖像時,Swift圖像更改爲錯誤圖像

我已經嘗試了所有可以在StackOverflow中找到的方法,包括將標記添加到原始數據然後檢查它但沒有奏效。我也在驗證應該用indexPath更新的單元格,並檢查單元格是否存在。所以我不知道爲什麼會發生這種情況。

這裏是我的代碼:

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 
    } 

回答

4

這是因爲UITableView的重用細胞。以這種方式加載它們會導致異步請求在不同的時間返回並搞亂訂單。

我建議你使用一些庫,這會讓你的生活變得更加簡單,像翠鳥。它會爲你下載和緩存圖像。你也不用擔心異步調用。

https://github.com/onevcat/Kingfisher

你與它的代碼會是這個樣子:

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

我試過使用AlamofireImage庫,應該已經解決了這個問題,但是我無法讓它工作,它對圖像有相同的效果。您介意解釋我應該使用Kingfisher庫的哪些方法? – BenNov

+0

我已經爲您的案例添加了示例代碼。這應該工作得很好。 –

+0

你怎麼聲明'imageView'? – BenNov

10

上的cellForRowAtIndexPath:

1)指定的索引值,以您的自定義單元格。例如,

cell.tag = indexPath.row 

2)在主線程,圖像分配前,檢查是否圖像通過與標籤匹配它所屬的相應單元格。

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

這對我來說是訣竅,同時在單元類代碼中將圖像視圖設置爲零。 – Supertecnoboff

+0

這種方法對我有好處。 – zippo

+0

簡單而有效! – iCediCe

2

UPDATE

在下面的代碼沒有工作,但後來我切換到使用KingFisher這是非常簡單的。

結束時更新

我不想使用第三方庫,如Alamofire和cell.tag我沒有工作。將初始圖像設置爲零,爲我做了訣竅。我也緩存了圖像。這是我寫的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() 
    } 
} 

可以在cellForRowAt調用此方法像這樣

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

Source1 Source2

0

對於這個問題,我有最好的解決辦法是斯威夫特3或Swift 4 只需編寫這兩行

cell.videoImage.image = nil 

cell.thumbnailimage.setImageWith(imageurl!)