在UI集合视图中滚动时,单元格重新加载错误的图像

时间:2017-02-05 17:18:15

标签: ios swift uicollectionview nscache

我使用DropBox的API下载图像并在集合视图中显示它们。当用户滚动时,图像将从单元格中消失,另一个图像将被加载或重新加载新图像并替换单元格中的图像。我怎样才能防止这种情况发生?我已经尝试过使用SDWebImage,这样可以保持图像的正确顺序,但每次图像在屏幕上滚动时图像都会消失并重新加载。另外,我直接下载图像,而不是从URL下载图像,我更喜欢不必编写一个可以使用SDWebImage的工作。

我发布了一个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()
}

4 个答案:

答案 0 :(得分:1)

因为TableView和CollectionView使用

配置新单元格时,

dequeueReusableCell(withReuseIdentifier: for indexPath:)功能,表格下的swift使用屏幕外的单元格来帮助手机内存,可能该单元格已经有了图像集必须处理这个案子。

我建议您查看方法“prepareCellForReuse”,在这种情况下,我认为您必须将imageView.image属性设置为nil。

我非常确定它会解决您的问题或给您正确的方向,但如果它不起作用请告诉我,我会尽力帮助您。

最佳结果。

答案 1 :(得分:1)

我修好了。它需要在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
}    

答案 2 :(得分:0)

使用SDWebImage并添加占位符图像:

cell.imageView.sd_setImage(with: URL(string: "http://www.domain.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))

答案 3 :(得分:0)

我发布了此信息,以防它对某人有所帮助。 我有一个集合视图(显示为垂直列表),其项目为集合视图(显示为水平单行网格)。滚动列表时,会重复子收集视图中的图像。

我通过将其放置在父集合视图的单元格类中解决了该问题。

override func prepareForReuse() {
    collectionView.reloadData()
    super.prepareForReuse()
}