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
Post a Comment