滚动时现有的CollectionView图像更改

时间:2017-08-27 14:56:09

标签: ios swift firebase uicollectionview

我已经查看了很多问题,我不相信这是重新使用单元格的结果,因为新的单元格图像是正确的,但现有的单元格图像不正确并且过去是正确的。我会先发布图像,这样问题就更容易理解了。

Before Scrolling

Existing Image Changed on Scroll

enter image description here

我有一个图像单元格的集合视图(类似于Instagrams用户页面)。我从Firebase获取所有数据。我在屏幕的初始加载时得到了前12个帖子。但是,如果快速向下滚动,则现有单元格图像将更改为新获取的图像。我不确定为什么会发生这种情况......也许这是一个缓存问题?该问题仅在您第一次加载屏幕时出现。我已经尝试将图像设置为nil,如下所示:

override func prepareForReuse() {
        super.prepareForReuse()
        self.imageView.image = UIImage()
    }  

但这对这个问题没有帮助。

这是我的cellForItemAt:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "imageCell", for: indexPath) as! ImageCell
        cell.indexPath = indexPath
        cell.imageView.downloadImage(from: currentTablePosts[indexPath.row].pathToImage)
        cell.layer.borderWidth = 1
        cell.layer.borderColor = UIColor.black.cgColor
        return cell
    }

图像下载和缓存:

let imageCache = NSCache<NSString, UIImage>()

extension UIImageView {

    func downloadImage(from imgURL: String!) {
        let url = URLRequest(url: URL(string: imgURL)!)

        // set initial image to nil so it doesn't use the image from a reused cell
        image = nil

        // check if the image is already in the cache
        if let imageToCache = imageCache.object(forKey: imgURL! as NSString) {
            self.image = imageToCache
            return
        }

        // download the image asynchronously
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error != nil {
                // user an alert to display the error
                if let topController = UIApplication.topViewController() {
                    Helper.showAlertMessage(vc: topController, title: "Error Downloading Image", message: error as! String)
                }
                return
            }

            DispatchQueue.main.async {
                // create UIImage
                let imageToCache = UIImage(data: data!)
                // add image to cache
                imageCache.setObject(imageToCache!, forKey: imgURL! as NSString)
                self.image = imageToCache
            }
        }
        task.resume()
    }
}

Firebase查询:

static func getInitialTablesPosts(tableNumber: String) {
        tableReference.child(tableNumber).queryLimited(toLast: 12).observeSingleEvent(of: .value, with: { snap in
            for child in snap.children {
                let child = child as? DataSnapshot
                if let post = child?.value as? [String: AnyObject] {
                    let posst = Post()
                    if let author = post["author"] as? String, let likes = post["likes"] as? Int, let pathToImage = post["pathToImage"] as? String, let postID = post["postID"] as? String, let postDescription = post["postDescription"] as? String, let timestamp = post["timestamp"] as? Double, let category = post["category"] as? String, let table = post["group"] as? String, let userID = post["userID"] as? String, let numberOfComments = post["numberOfComments"] as? Int, let region = post["region"] as? String {

                        posst.author = author
                        posst.likes = likes
                        posst.pathToImage = pathToImage
                        posst.postID = postID
                        posst.userID = userID
                        posst.fancyPostDescription = Helper.createAttributedString(author: author, postText: postDescription)
                        posst.postDescription = author + ": " + postDescription
                        posst.timestamp = timestamp
                        posst.table = table
                        posst.region = region
                        posst.category = category
                        posst.numberOfComments = numberOfComments
                        posst.userWhoPostedLabel = Helper.createAttributedPostLabel(username: author, table: table, region: region, category: category)

                        if let people = post["peopleWhoLike"] as? [String: AnyObject] {
                            for(_, person) in people {
                                posst.peopleWhoLike.append(person as! String)
                            }
                        }
                        currentTablePosts.insert(posst, at: 0)
                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTableCollectionView"), object: nil)
                    } // end of if let
                }
            }
        })
        tableReference.removeAllObservers()
    }

    static func getMoreTablePosts(tableNumber: String, lastVisibleKey: String) {
        print("FIRED...")
        let currentNumberOfPosts = currentTablePosts.count
        print("Number of posts before fetiching ", currentNumberOfPosts)
        print("Oldest post key ", oldestTableKeys[tableNumber] ?? "not set yet", "***********")

        tableReference.child(tableNumber).queryOrderedByKey().queryEnding(atValue: lastVisibleKey).queryLimited(toLast: 12).observeSingleEvent(of: .value, with: { snap in

            for child in snap.children {
                let child = child as? DataSnapshot
                if let post = child?.value as? [String: AnyObject] {
                    if let id = post["postID"] as? String {
                        if id == lastVisibleKey {
                            return
                        }
                    }
                    let posst = Post()
                    if let author = post["author"] as? String, let likes = post["likes"] as? Int, let pathToImage = post["pathToImage"] as? String, let postID = post["postID"] as? String, let postDescription = post["postDescription"] as? String, let timestamp = post["timestamp"] as? Double, let category = post["category"] as? String, let table = post["group"] as? String, let userID = post["userID"] as? String, let numberOfComments = post["numberOfComments"] as? Int, let region = post["region"] as? String {

                        posst.author = author
                        posst.likes = likes
                        posst.pathToImage = pathToImage
                        posst.postID = postID
                        posst.userID = userID
                        posst.fancyPostDescription = Helper.createAttributedString(author: author, postText: postDescription)
                        posst.postDescription = author + ": " + postDescription
                        posst.timestamp = timestamp
                        posst.table = table
                        posst.region = region
                        posst.category = category
                        posst.numberOfComments = numberOfComments
                        posst.userWhoPostedLabel = Helper.createAttributedPostLabel(username: author, table: table, region: region, category: category)

                        if let people = post["peopleWhoLike"] as? [String: AnyObject] {
                            for(_, person) in people {
                                posst.peopleWhoLike.append(person as! String)
                            }
                        }
                        currentTablePosts.insert(posst, at: currentNumberOfPosts)
                        NotificationCenter.default.post(name: NSNotification.Name(rawValue: "reloadTableCollectionView"), object: nil)
                        if let oldestTableKey = oldestTableKeys[tableNumber] {
                            if postID == oldestTableKey {
                                print("returning")
                                print("number of posts on return \(currentTablePosts.count)")
                                return
                            }
                        }
                    } // end if let
                }
            }
        })
        tableReference.removeAllObservers()
    }

1 个答案:

答案 0 :(得分:0)

*更新*

此处使用的图像缓存在我的体验中仍然存在一些问题。我已经开始使用Kingfisher,这非常容易设置和使用。

*旧解决方案*

根据评论中建议的答案找到solution

我修改了用于缓存图片的扩展程序。虽然将来我想我会继承UIImageView。这是我的代码的修改版本。

import UIKit

let userImageCache = NSCache<NSString, UIImage>()
let imageCache = NSCache<AnyObject, AnyObject>()
var imageURLString: String?


extension UIImageView {

    public func imageFromServerURL(urlString: String, collectionView: UICollectionView, indexpath : IndexPath) {
        imageURLString = urlString

        if let url = URL(string: urlString) {
            image = nil
            if let imageFromCache = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
                self.image = imageFromCache
                return
            }

            URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
                if error != nil{
                    if let topController = UIApplication.topViewController() {
                        Helper.showAlertMessage(vc: topController, title: "Error Downloading Image", message: error as! String)
                    }
                    return
                }

                DispatchQueue.main.async(execute: {
                    if let imgaeToCache = UIImage(data: data!){
                        if imageURLString == urlString {
                            self.image = imgaeToCache
                        }
                        imageCache.setObject(imgaeToCache, forKey: urlString as AnyObject)// calls when scrolling
                        collectionView.reloadItems(at: [indexpath])
                    }
                })
            }) .resume()
        }
    }

    func downloadImage(from imgURL: String!) {
        let url = URLRequest(url: URL(string: imgURL)!)

        // set initial image to nil so it doesn't use the image from a reused cell
        image = nil

        // check if the image is already in the cache
        if let imageToCache = imageCache.object(forKey: imgURL! as AnyObject) as? UIImage {
            self.image = imageToCache
            return
        }

        // download the image asynchronously
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error != nil {
                // user an alert to display the error
                if let topController = UIApplication.topViewController() {
                    Helper.showAlertMessage(vc: topController, title: "Error Downloading Image", message: error as! String)
                }
                return
            }

            DispatchQueue.main.async {
                let imageToCache = UIImage(data: data!)
                imageCache.setObject(imageToCache!, forKey: imgURL! as AnyObject)
                self.image = imageToCache
            }
        }
        task.resume()
    }

    func downloadUserImage(from imgURL: String!) {
        let url = URLRequest(url: URL(string: imgURL)!)

        // set initial image to nil so it doesn't use the image from a reused cell
        image = nil

        // check if the image is already in the cache
        if let imageToCache = userImageCache.object(forKey: imgURL! as NSString) {
            self.image = imageToCache
            return
        }

        // download the image asynchronously
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if error != nil {
                // user an alert to display the error
                if let topController = UIApplication.topViewController() {
                    Helper.showAlertMessage(vc: topController, title: "Error Downloading Image", message: error as! String)
                }
                return
            }

            DispatchQueue.main.async {
                // create UIImage
                let imageToCache = UIImage(data: data!)
                // add image to cache
                userImageCache.setObject(imageToCache!, forKey: imgURL! as NSString)
                self.image = imageToCache
            }
        }
        task.resume()
    }
}

我创建了第一个缓存collectionView图像的方法,其他方法仍然用于tableViews。缓存也已从let imageCache = NSCache<NSString, UIImage>()更改为let imageCache = NSCache<AnyObject, AnyObject>()