我是swift和iOS編程的新手,但是我已經能夠在Xcode中爲我的應用程序構建一個基本穩定的啓動界面。 CollectionView從我的家庭網絡服務器上的csv文件創建的字典數組中抓取圖像和文本。 CVS的文件包含以下格式的數據(注意,網址已被更改,以保護許可圖像):swift iOS - UICollectionView圖像在快速滾動後混合
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
的問題是,通過的CollectionView快速滾動時,細胞將抓住不正確的圖像。值得注意的是,如果您進行慢速或中速滾動,則會在將正確的圖像渲染到正確的單元格之前顯示不同的圖像(單元格的標籤文本始終正確,只有圖像永遠不會顯示)。在圖像不匹配的情況下,出現標籤的正確單元格後,CollectionView中的所有其他單元格也將顯示不正確的圖像。
E.g.單元1-9將顯示Title1-9和正確的圖像1-9 當滾動緩慢時,單元格19-27將顯示標題19-27,將簡要顯示圖像10-18,然後顯示正確的圖像19-27。 快速滾動大量的單元格時(例如從單元格1-9到單元格90-99),單元格90-99將顯示標題90-99,將顯示圖像10-50ish,然後將錯誤地保留在圖像41- 50(或其附近)。當進一步滾動時,單元格100+將顯示正確的標題,但僅顯示圖像41-50範圍內的圖像。
我認爲這個錯誤是因爲沒有正確處理單元重用,圖像緩存處理不當,或者兩者兼而有之。這也可能是我沒有看到作爲初學者的iOS /快速程序員。我試圖用完成修飾符實現一個請求,但似乎無法讓我的代碼設置的方式正常工作。我將不勝感激任何幫助,以及解釋爲什麼修復程序的工作方式。謝謝!
相關代碼如下。
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!
}
單元格被重用並且您正在異步調度圖像獲取,因此當您詭計單元並異步獲取新圖像時,舊獲取仍在運行。你需要處理這個。可能最簡單的方法是使用SDWebImage,它在UIImageView上有一個擴展名,可以爲你處理這個問題 – Paulw11
@ Paulw11:是的,我認爲它必須處理沒有被正確取消的請求。我希望能夠使用UIKit編寫代碼,因爲我使用這個應用程序作爲學習體驗,並且我覺得依靠外部框架會失敗,因爲這是一個捷徑。雖然我確實看過Alamofire/AlamofireImage和SDWebImage,但由於某些原因,在安裝了pod之後,我無法導入模塊(這是一個單獨的問題,我還沒弄清楚)。如果我需要的話,我會用它,只是想先嚐試用UIKit來處理它。 – shadowofjazz
另一種方法是將圖像URL存儲爲您單元格的屬性,然後在完成關閉中,根據您剛剛獲取的URL檢查屬性值。如果它不匹配,則不要設置圖像,因爲單元格已被重新使用。 – Paulw11