swiftuiimageviewuicollectionviewcellselecteditemimage-caching

load selectedIndexItem in new collectionViewCell (invalidate imageCache)


I am having a lot of trouble loading a CustomImageView: UIImageView in a detail viewController based on the selectedIndexPath of a collectionViewCell. I have successfully passed and loaded the UILabels and UITextViews, but am not able to load the CustomImageView:UIImageView from that same selectedIndexPath using the same process and code logic. I believe it has something to do with clearing or resetting my image cache, but not sure where or exactly what code to execute to do this. Sorry for any excess code, just want to be thorough. Thanks for any help or direction!

// model object classes storing values from Firebase

class CurrentPlanner: SafeJsonObjectPlanner {

    var name: String?
    var profileImageUrl: String?
    var planningPlace: String?

    init(dictionary: [String: AnyObject]) {
        self.name = dictionary["addedBy"] as? String
        self.profileImageUrl = dictionary["profileImageUrl"] as? String
        self.planningPlace = dictionary["planning"] as? String
    }
}

// CustomImageView: UIImageView extension that populates the first collectionViewController

let imageCache = NSCache<NSString, UIImage>()

class CustomImageView: UIImageView {

    var imageUrlString: String?

    func loadImageUsingUrlString(_ urlString: String) {

        imageUrlString = urlString
        let url = URL(string: urlString)
        image = nil

        if let imageFromCache = imageCache.object(forKey: urlString as NSString) {
            self.image = imageFromCache
            return
        }

        URLSession.shared.dataTask(with: url!, completionHandler: { (data, respones, error) in

            if error != nil {
                print(error!)
                return
            }

            DispatchQueue.main.async(execute: {
                let imageToCache = UIImage(data: data!)
                if self.imageUrlString == urlString {
                    self.image = imageToCache
                }
                imageCache.setObject(imageToCache!, forKey: urlString as NSString)
            })
        }).resume()
    }
}

extension UIImageView {
    func loadImageUsingCacheWithUrlString(_ urlString: String) {
        self.image = nil

        //check cache for image first
        if let cachedImage = imageCache.object(forKey: urlString as NSString) {
            self.image = cachedImage
            return
        }

        //otherwise fire off a new download
        let url = URL(string: urlString)
        URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in

            //download hit an error so lets return out
            if let error = error {
                print(error)
                return
            }

            DispatchQueue.main.async(execute: {
                if let downloadedImage = UIImage(data: data!) {
                    imageCache.setObject(downloadedImage, forKey: urlString as NSString)
                    self.image = downloadedImage
                }
            })
        }).resume()
    }
}

// planning cell class in the first collectionView

class BasePlanningCell: BaseCell2 {

    var currentPlanners = [CurrentPlanner]()

    var currentPlanner: CurrentPlanner? {
        didSet {
            setupProfileImage()
        }
    }

    fileprivate func setupProfileImage() {
        if let profileImageUrl = currentPlanner?.profileImageUrl {        
 userProfileImageView.loadImageUsingCacheWithUrlString(profileImageUrl)
        }
    }

// tapped cell class in first collectionView - call delegate method

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        print("cell tapped")
        // cell delegate method called from collectionViewController
        travelersFeedVC?.showPlanningViewDetailView(indexPath: indexPath)
    }

// first collectionViewController class - method performed on cell tap

    func showPlanningViewDetailView(indexPath: IndexPath) {
        let plannersDetailVC = PlanningPlaceDetailsVC()

        plannersDetailVC.profileImageUrl = plannedPlaces[indexPath.row].profileImageUrl!
        print(plannersDetailVC.profileImageUrl!)

        show(plannersDetailVC, sender: self)
    }

// 2nd DetailViewController class

    var nameString: String!
    var locationString: String!
    var profileImageUrl: String!

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        switch indexPath.item {
        case 0:
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PlanningDetailViewCells", for: indexPath) as! PlanningDetailViewCells
            cell.myMethod(str: nameString)
            cell.getLocationMethod(str: locationString)
            cell.getProfileImageMethod(str: profileImageUrl)
            return cell
        case 1:
            // ...
        case 2:
             // ...
        default:
             // ...
        }
    }

// 2nd DetailViewControllers Cells Delegate Class

    func myMethod(str : String){
        nameString = str
        print("var : \(nameString)")
        planningCellHeader?.titleLabel.text = nameString
    }

    func getLocationMethod(str : String){
        locationString = str
        print("var : \(String(describing: locationString))")
        planningCellHeader?.locationTextView.text = locationString
    }

    func getProfileImageMethod(str : String){
        profileImageUrl = str
        print("var : \(String(describing: profileImageUrl))")
        planningCellHeader?.userProfileImageView.imageUrlString = profileImageUrl
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

        switch indexPath.section {
        case 0:
            switch indexPath.item {
            case 0:
                let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "headerId", for: indexPath) as! PlanningCellHeader                
                cell.myMethod(str: nameString)
                cell.getLocationMethod(str: locationString)
                cell.getProfileImageMethod(str: profileImageUrl)
                return cell
            case 1:
                // ...
            default:
                // ...
            }
        default:
            // ...
        }
    }

// headerCell class that holds the views

var nameString: String?
var locationString: String?
var profileImageUrl: String?

    let titleLabel: UILabel = {
        let label = UILabel()
        // ...
        return label
    }()

    let locationTextView: UITextView = {
        let textView = UITextView()
        // ...
        return textView
    }()

    let userProfileImageView: CustomImageView = {
        let imageView = CustomImageView()
        // ...
        return imageView
    }()

    override init(frame: CGRect) {
        super.init(frame: frame)

        titleLabel.text = nameString
        locationTextView.text = locationString
        userProfileImageView.imageUrlString = profileImageUrl
        setupViews()
    }
        func myMethod(str : String){
        nameString = str
        print("var : \(String(describing: nameString))")
        titleLabel.text = nameString
    }

    func getLocationMethod(str : String){
        locationString = str
        print("var : \(String(describing: locationString))")
        locationTextView.text = locationString
    }

    func getProfileImageMethod(str : String){
        profileImageUrl = str
        print("var : \(String(describing: profileImageUrl))")
//        userProfileImageView.image = UIImage(named: "meAndDuncan")
        userProfileImageView.imageUrlString = profileImageUrl
    }

Solution

  • I figured it out on my own. Maybe self explanatory, but I had to convert the imageUrlString back to Data(contentsOf: url!) in my getProfileImageMethod(str : String) as seen below:

    func getProfileImageMethod(str : String){
        profileImageUrl = str
        print("var : \(String(describing: profileImageUrl))")
        // added the code below and it worked!
        var imageUrlString: String?
        imageUrlString = profileImageUrl
        let url = URL(string: imageUrlString!)
        let data = try? Data(contentsOf: url!)
        let image: UIImage = UIImage(data: data!)!
    
        userProfileImageView.image = image
    }