swift iOS - UICollectionView images mixed up after fast scroll -


i new swift , ios programming, have been able build stable starting interface application in xcode. collectionview grabs image , text array of dictionaries created csv file on home network server. cvs file contains data in following format (note, urls have been changed protect licensed images):

csv file

csv file @ url https://myserver.com/csv.txt , contains following  title";"seriesimageurl title1";"https://licensedimage.com/url1 title2";"https://licensedimage.com/url2 ... title1000";"https://licensedimage.com/url1000 

the problem when scrolling through collectionview, cell grab incorrect image. noticeably, if slow or medium scroll, different image show before correct image rendered correct cell (the label text cells correct, image ever off). after mismatch of images proper cell label occurs, other cells in collectionview have incorrect images displayed.

e.g. cell 1-9 show title1-9 correct image1-9 when scrolling slowly, cells 19-27 show title 19-27, briefly show image 10-18 , show correct image 19-27. when scrolling huge number of cells (e.g. cell 1-9 cell 90-99), cells 90-99 show title 90-99, show image 10-50ish, , incorrectly stay on image 41-50(or thereabout). when scrolling further, cells 100+ display correct title show images range image 41-50.

i think error either because cell reuse isn't handled properly, caching of images isn't handled properly, or both. not seeing beginner ios/swift programmer. have tried implement request completion modifier cannot seem working way code set up. appreciate explanation why fix works way does. thanks!

the relevant code below.

seriescollectionviewcontroller.swift

class seriescollectionviewcontroller: uicollectionviewcontroller, uisearchbardelegate {  let reuseidentifier:string = "seriescell"  // set data source models & variables struct seriesmodel {      let title: anyobject     let seriesimageurl: anyobject } var seriesdict = [string:anyobject]() var seriesarray = [seriesmodel]()  // image cache var imagecache = nscache()   override func viewdidload() {     super.viewdidload()     // grab data source     {          let url = nsurl(string: "https://myserver.com/csv.txt")         let fulltext = try nsstring(contentsofurl: url!, encoding: nsutf8stringencoding)         let readings = fulltext.componentsseparatedbystring("\n") [string]         var seriesdictcount = readings.count         seriesdictcount -= 1         in 1..<seriesdictcount {             let seriesdata = readings[i].componentsseparatedbystring("\";\"")             seriesdict["title"] = "\(seriesdata[0])"             seriesdict["seriesimageurl"] = "\(seriesdata[1])"             seriesarray.append(seriesmodel(                 title: seriesdict["title"]!,                 seriesimageurl: seriesdict["seriesimageurl"]!,             ))         }     } catch let error nserror {         print("error: \(error)")     } }  override func didreceivememorywarning() {     super.didreceivememorywarning()     imagecache.removeallobjects()     // dispose of resources can recreated. }  //... //...skipping on stuff isn't relevant //...  override func collectionview(collectionview: uicollectionview, cellforitematindexpath indexpath: nsindexpath) -> seriescollectionviewcell {     let cell: seriescollectionviewcell = collectionview.dequeuereusablecellwithreuseidentifier(reuseidentifier, forindexpath: indexpath) as! seriescollectionviewcell      if (self.searchbaractive) {         let series = seriesarrayforsearchresult[indexpath.row]         {             // set image             if let imageurl = nsurl(string: "\(series.seriesimageurl)") {                 if let image = imagecache.objectforkey(imageurl) as? uiimage {                         cell.seriesimage.image = image                 } else {                     dispatch_async(dispatch_get_global_queue(dispatch_queue_priority_background, 0), {                         if let tvimagedata = nsdata(contentsofurl: imageurl) {                             let image = uiimage(data: tvimagedata)                             self.imagecache.setobject(image!, forkey: imageurl)                                 dispatch_async(dispatch_get_main_queue(), { () -> void in                                     cell.seriesimage.image = nil                                     cell.seriesimage.image = image                                 })                         }                     })                 }             }             cell.serieslabel.text = "\(series.title)"         }     } else {         let series = seriesarray[indexpath.row]         {             // set image             if let imageurl = nsurl(string: "\(series.seriesimageurl)") {                 if let image = imagecache.objectforkey(imageurl) as? uiimage {                         cell.seriesimage.image = image                 } else {                     dispatch_async(dispatch_get_global_queue(dispatch_queue_priority_background, 0), {                         if let tvimagedata = nsdata(contentsofurl: imageurl) {                             let image = uiimage(data: tvimagedata)                             self.imagecache.setobject(image!, forkey: imageurl)                             dispatch_async(dispatch_get_main_queue(), { () -> void in                                 cell.seriesimage.image = nil                                 cell.seriesimage.image = image                             })                         }                     })                 }              }             cell.serieslabel.text = "\(series.title)"         }     }     cell.layer.shouldrasterize = true     cell.layer.rasterizationscale = uiscreen.mainscreen().scale     cell.prepareforreuse()     return cell } 

seriescollectionviewcell

class seriescollectionviewcell: uicollectionviewcell {  @iboutlet weak var seriesimage: uiimageview! @iboutlet weak var serieslabel: uilabel!  } 

you need understand how dequeue works properly. there articles this.

to summarise:

to maintain scrolling smoothness, dequeue used reuses cells after limit. have 10 visible cells @ time, create 16-18 (3-4 above, 3-4 below, rough estimates) cells only, though might need 1000 cells.

now when doing this-

let cell: seriescollectionviewcell = collectionview.dequeuereusablecellwithreuseidentifier(reuseidentifier, forindexpath: indexpath) as! seriescollectionviewcell 

you reusing existing cell made cells. that's why see old image time , see new image once it's loaded.

you need clear old image dequeue cell.

cell.seriesimage.image = uiimage()    //nil 

you set blank placeholder way in image , not mess imagecache being nil in case.

that solves problem-

noticeably, if slow or medium scroll, different image show before correct image rendered correct cell (the label text cells correct, image ever off).

now when assigning image current cell, need check again, if image you're assigning belongs cell or not. need check because image view you're assigning being reused , might associated cell @ indexpath different of when image load request generated.

when dequeue cell , set image property nil, ask seriesimage remember current indexpath you.

cell.seriesimage.indexpath = indexpath 

later, assign image it, if imageview still belongs assigned indexpath. works 100% cell reuse.

if cell.seriesimage.indexpath == indexpath cell.seriesimage.image = image 

you might need consider setting indexpath on uiimageview instance. here's prepared , use similar scenario- uiview-additions

it available in both objective-c & swift.


Comments

Popular posts from this blog

sequelize.js - Sequelize group by with association includes id -

android - Robolectric "INTERNET permission is required" -

java - Android raising EPERM (Operation not permitted) when attempting to send UDP packet after network connection -