在 UI Collection 视图中滚动时,单元格会重新加载错误的图像
Cells reload wrong images when scrolled in UI Collection View
我正在使用 DropBox API 下载图像并在 Collection 视图中显示它们。当用户滚动时,图像要么从单元格中消失并加载另一个图像,要么重新加载新图像并替换单元格中的图像。我怎样才能防止这种情况发生?我试过使用 SDWebImage,这使图像保持正确的顺序,但图像仍然会消失并在每次滚动出屏幕时重新加载。另外,我直接下载图像,而不是从 URL 下载图像,我宁愿不必编写 work-a-round 就能够使用 SDWebImage。
我想 post 一个 gif 作为示例,但我的声誉太低了。
欢迎任何帮助:)
var filenames = [String]()
var selectedFolder = ""
// image cache
var imageCache = NSCache<NSString, UIImage>()
override func viewDidLoad() {
super.viewDidLoad()
getFileNames { (names, error) in
self.filenames = names
if error == nil {
self.collectionView?.reloadData()
print("Gathered filenames")
}
}
collectionView?.collectionViewLayout = gridLayout
collectionView?.reloadData()
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
}
func getFileNames(completion: @escaping (_ names: [String], _ error: Error?) -> Void) {
let client = DropboxClientsManager.authorizedClient!
client.files.listFolder(path: "\(selectedFolder)", recursive: false, includeMediaInfo: true, includeDeleted: false, includeHasExplicitSharedMembers: false).response { response, error in
var names = [String]()
if let result = response {
for entry in result.entries {
if entry.name.hasSuffix("jpg") {
names.append(entry.name)
}
}
} else {
print(error!)
}
completion(names, error as? Error)
}
}
func checkForNewFiles() {
getFileNames { (names, error) in
if names.count != self.filenames.count {
self.filenames = names
self.collectionView?.reloadData()
}
}
}
func downloadFiles(fileName: String, completion:@escaping (_ image: UIImage?, _ error: Error?) -> Void) {
if let cachedImage = imageCache.object(forKey: fileName as NSString) as UIImage? {
print("using a cached image")
completion(cachedImage, nil)
} else {
let client = DropboxClientsManager.authorizedClient!
client.files.download(path: "\(selectedFolder)\(fileName)").response { response, error in
if let theResponse = response {
let fileContents = theResponse.1
if let image = UIImage(data: fileContents) {
// resize the image here and setObject the resized Image to save it to cache.
// use resized image for completion as well
self.imageCache.setObject(image, forKey: fileName as NSString)
completion(image, nil) // completion(resizedImage, nil)
}
else {
completion(nil, error as! Error?)
}
} else if let error = error {
completion(nil, error as? Error)
}
}
.progress { progressData in
}
}
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.filenames.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ImageCell
cell.backgroundColor = UIColor.lightGray
let fileName = self.filenames[indexPath.item]
let cellIndex = indexPath.item
self.downloadFiles(fileName: fileName) { (image, error) in
if cellIndex == indexPath.item {
cell.imageCellView.image = image
print("image download complete")
}
}
return cell
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
gridLayout.invalidateLayout()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
imageCache.removeAllObjects()
}
因为 TableView 和 CollectionView 使用
dequeueReusableCell(withReuseIdentifier: for indexPath:)
函数当你配置一个新的单元格时,swift在table下所做的是使用屏幕外的单元格来帮助记忆你的phone 可能那个单元格已经有一个图像集,你必须处理这种情况。
我建议您查看方法“prepareCellForReuse
”,在这种情况下,我认为您需要做的是将 imageView.image 属性设置为 nil。
我很确定它会解决你的问题或给你正确的方向,但如果它不起作用请告诉我,我会尽力帮助你。
最好的结果。
使用 SDWebImage 并添加占位符图像:
cell.imageView.sd_setImage(with: URL(string: "http://www.domain.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
我修好了。它需要在 cellForItemAt 函数中设置单元格图像 = nil,如果用户在完成下载之前将单元格滚动到屏幕外,则取消图像请求。
这是新的 cellForItemAt 代码:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let fileId = indexPath.item
let fileName = self.filenames[indexPath.item]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ImageCell
cell.backgroundColor = UIColor.lightGray
if cell.request != nil {
print("request not nil; cancel ", fileName)
}
cell.request?.cancel()
cell.request = nil
cell.imageCellView.image = nil
print ("clear image ", fileId)
self.downloadFiles(fileId:fileId, fileName: fileName, cell:cell) { (image, error) in
guard let image = image else {
print("abort set image ", fileId)
return
}
cell.imageCellView.image = image
print ("download/cache: ", fileId)
}
return cell
}
我post 这个可能对某人有帮助。
我有一个集合视图(显示为垂直列表),其项目是集合视图(显示为水平单线网格)。滚动列表时重复子集合视图中的图像。
我通过将它放在父集合视图的 class 单元格中解决了这个问题。
override func prepareForReuse() {
collectionView.reloadData()
super.prepareForReuse()
}
我正在使用 DropBox API 下载图像并在 Collection 视图中显示它们。当用户滚动时,图像要么从单元格中消失并加载另一个图像,要么重新加载新图像并替换单元格中的图像。我怎样才能防止这种情况发生?我试过使用 SDWebImage,这使图像保持正确的顺序,但图像仍然会消失并在每次滚动出屏幕时重新加载。另外,我直接下载图像,而不是从 URL 下载图像,我宁愿不必编写 work-a-round 就能够使用 SDWebImage。
我想 post 一个 gif 作为示例,但我的声誉太低了。
欢迎任何帮助:)
var filenames = [String]()
var selectedFolder = ""
// image cache
var imageCache = NSCache<NSString, UIImage>()
override func viewDidLoad() {
super.viewDidLoad()
getFileNames { (names, error) in
self.filenames = names
if error == nil {
self.collectionView?.reloadData()
print("Gathered filenames")
}
}
collectionView?.collectionViewLayout = gridLayout
collectionView?.reloadData()
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
}
func getFileNames(completion: @escaping (_ names: [String], _ error: Error?) -> Void) {
let client = DropboxClientsManager.authorizedClient!
client.files.listFolder(path: "\(selectedFolder)", recursive: false, includeMediaInfo: true, includeDeleted: false, includeHasExplicitSharedMembers: false).response { response, error in
var names = [String]()
if let result = response {
for entry in result.entries {
if entry.name.hasSuffix("jpg") {
names.append(entry.name)
}
}
} else {
print(error!)
}
completion(names, error as? Error)
}
}
func checkForNewFiles() {
getFileNames { (names, error) in
if names.count != self.filenames.count {
self.filenames = names
self.collectionView?.reloadData()
}
}
}
func downloadFiles(fileName: String, completion:@escaping (_ image: UIImage?, _ error: Error?) -> Void) {
if let cachedImage = imageCache.object(forKey: fileName as NSString) as UIImage? {
print("using a cached image")
completion(cachedImage, nil)
} else {
let client = DropboxClientsManager.authorizedClient!
client.files.download(path: "\(selectedFolder)\(fileName)").response { response, error in
if let theResponse = response {
let fileContents = theResponse.1
if let image = UIImage(data: fileContents) {
// resize the image here and setObject the resized Image to save it to cache.
// use resized image for completion as well
self.imageCache.setObject(image, forKey: fileName as NSString)
completion(image, nil) // completion(resizedImage, nil)
}
else {
completion(nil, error as! Error?)
}
} else if let error = error {
completion(nil, error as? Error)
}
}
.progress { progressData in
}
}
}
override func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return self.filenames.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ImageCell
cell.backgroundColor = UIColor.lightGray
let fileName = self.filenames[indexPath.item]
let cellIndex = indexPath.item
self.downloadFiles(fileName: fileName) { (image, error) in
if cellIndex == indexPath.item {
cell.imageCellView.image = image
print("image download complete")
}
}
return cell
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)
gridLayout.invalidateLayout()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
imageCache.removeAllObjects()
}
因为 TableView 和 CollectionView 使用
dequeueReusableCell(withReuseIdentifier: for indexPath:)
函数当你配置一个新的单元格时,swift在table下所做的是使用屏幕外的单元格来帮助记忆你的phone 可能那个单元格已经有一个图像集,你必须处理这种情况。
我建议您查看方法“prepareCellForReuse
”,在这种情况下,我认为您需要做的是将 imageView.image 属性设置为 nil。
我很确定它会解决你的问题或给你正确的方向,但如果它不起作用请告诉我,我会尽力帮助你。
最好的结果。
使用 SDWebImage 并添加占位符图像:
cell.imageView.sd_setImage(with: URL(string: "http://www.domain.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
我修好了。它需要在 cellForItemAt 函数中设置单元格图像 = nil,如果用户在完成下载之前将单元格滚动到屏幕外,则取消图像请求。 这是新的 cellForItemAt 代码:
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let fileId = indexPath.item
let fileName = self.filenames[indexPath.item]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! ImageCell
cell.backgroundColor = UIColor.lightGray
if cell.request != nil {
print("request not nil; cancel ", fileName)
}
cell.request?.cancel()
cell.request = nil
cell.imageCellView.image = nil
print ("clear image ", fileId)
self.downloadFiles(fileId:fileId, fileName: fileName, cell:cell) { (image, error) in
guard let image = image else {
print("abort set image ", fileId)
return
}
cell.imageCellView.image = image
print ("download/cache: ", fileId)
}
return cell
}
我post 这个可能对某人有帮助。 我有一个集合视图(显示为垂直列表),其项目是集合视图(显示为水平单线网格)。滚动列表时重复子集合视图中的图像。
我通过将它放在父集合视图的 class 单元格中解决了这个问题。
override func prepareForReuse() {
collectionView.reloadData()
super.prepareForReuse()
}