Sono nuovo alla programmazione rapida e iOS, ma sono riuscito a creare un'interfaccia di avvio sostanzialmente stabile per la mia applicazione in Xcode. CollectionView acquisisce un'immagine e un testo da una serie di dizionari creati da un file CSV sul mio server di rete domestico. Il file CVS contiene i dati nel seguente formato (nota, gli URL sono stati cambiati per proteggere le immagini con licenza):swift iOS - UICollectionVisualizzazione delle immagini dopo lo scorrimento veloce
file CSV
csv file is @ url https://myserver.com/csv.txt and contains the following
Title";"SeriesImageURL
Title1";"https://licensedimage.com/url1
Title2";"https://licensedimage.com/url2
...
Title1000";"https://licensedimage.com/url1000
Il problema è che quando si scorre rapidamente attraverso il CollectionView, la cella catturerà l'immagine errata. Notevolmente, se si esegue uno scorrimento lento o medio, verrà visualizzata un'immagine diversa prima che l'immagine corretta venga resa nella cella corretta (il testo dell'etichetta per le celle è sempre corretto, solo l'immagine è sempre disattivata). Dopo che si è verificata la mancata corrispondenza delle immagini con la cella corretta con etichetta, tutte le altre celle di CollectionView mostreranno anche immagini errate.
E.g. La cella 1-9 mostrerà il Titolo 1-9 con la corretta Image1-9 Quando si scorre lentamente, Cell 19-27 mostrerà il Titolo 19-27, mostrerà brevemente l'Immagine 10-18 e mostrerà l'immagine corretta 19-27. Quando si scorre velocemente un numero enorme di celle (ad es. Dalla cella 1-9 alla cella 90-99), le celle 90-99 mostreranno il Titolo 90-99, mostreranno l'Immagine 10-50, e quindi rimarranno erroneamente sull'immagine 41- 50 (o circa). Quando si scorre ulteriormente, Cell 100+ visualizzerà il titolo corretto ma mostrerà solo le immagini dalla gamma Immagine 41-50.
Penso che questo errore sia perché il riutilizzo delle celle non è gestito correttamente, la memorizzazione nella cache delle immagini non è gestita correttamente, o entrambi. Potrebbe anche essere qualcosa che non vedo come programmatore iOS/Swift per principianti. Ho provato a implementare una richiesta con un modificatore di completamento ma non riesco a farlo funzionare correttamente con il modo in cui il mio codice è impostato. Gradirei qualsiasi aiuto con questo, oltre a una spiegazione del motivo per cui la correzione funziona come fa. Grazie!
Il codice pertinente è di seguito.
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 from Source
do {
let url = NSURL(string: "https://myserver.com/csv.txt")
let fullText = try NSString(contentsOfURL: url!, encoding: NSUTF8StringEncoding)
let readings = fullText.componentsSeparatedByString("\n") as [String]
var seriesDictCount = readings.count
seriesDictCount -= 1
for i 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 as NSError {
print("Error: \(error)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
imageCache.removeAllObjects()
// Dispose of any resources that can be recreated.
}
//...
//...skipping over some stuff that 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]
do {
// 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]
do {
// 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!
}
Le celle vengono riutilizzate e si sta rimuovendo l'immagine in modo asincrono, quindi quando si riordina la cella e si recupera in modo asincrono una nuova immagine, il vecchio recupero è ancora in esecuzione. Devi gestirlo. Probabilmente il modo più semplice è usare qualcosa di SDWebImage che ha un'estensione su UIImageView che gestirà questo per te – Paulw11
@ Paulw11: Sì, ho pensato che avesse a che fare con le richieste non cancellate correttamente o qualcosa del genere. Speravo di scrivere il codice usando UIKit visto che sto usando questa applicazione come esperienza di apprendimento e mi sento di fare affidamento su un framework esterno che sconfigge quello scopo dato che si tratta di una scorciatoia. In realtà ho analizzato Alamofire/AlamofireImage e SDWebImage, ma per qualche motivo dopo l'installazione dei pod non riesco a importare nei moduli (questo è un problema separato che non ho ancora capito). Lo userò se ne avrò bisogno, voglio solo provare a gestirlo prima con UIKit. – shadowofjazz
Un altro approccio è di memorizzare l'URL dell'immagine come una proprietà della cella, quindi nella chiusura del completamento, controlla il valore della proprietà rispetto all'URL appena recuperato. Se non corrisponde, non impostare l'immagine poiché la cella è già stata riutilizzata. – Paulw11