Home > other >  How to display an icon/image on uicollectionviewcell after it has been selected
How to display an icon/image on uicollectionviewcell after it has been selected

Time:01-04

I have a uiCollectionViewCell which loads image from an api. I want to display another image/icon on the cell when a user clicks on it. In my custom cell I have two images one which display the image from the URL and the second one is the one I would like to show if the user has clicked on it. I'm doing this to alert the user that they have selected that cell. Below is my sample code

protocol ModalDelegate {
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String)

}

class GuestRateMovieView: UIViewController, ModalDelegate {
func changeValue(userChoice: String, rateMovieID: String, rateImageUrl: String, title: String) {
    self.userChoice = userChoice
    totalRated = totalRated   1
    lblRated.text = "\(totalRated) rated"
    if totalRated > 0 {
        ratedView.backgroundColor = .ratedGoldColour
    }else{
        ratedView.backgroundColor = .white
    }
    if totalRated >= 5 {
        btnFloatNext.alpha = 1
    }
    if totalRated > 5 {
        userChoiceMovieImage.sd_setImage(with: URL(string: rateImageUrl), placeholderImage: UIImage(named: "ImagePlaceholder"))
        lblUserChoice.text = "Great taste. We love the \(title) too."
    }
    var rating = 1
    if userChoice == "Hate it"{
        rating = 1
    }else if userChoice == "Good" {
        rating = 3
    }else{
        rating = 5
    }
    
    let guestRatingValues = GuestUserRate(id: rateMovieID, imageUrl: rateImageUrl, userRate: rating)
// caching rating
    GuestRateMovieView.createUserRating(guestRatingValues)
    
}

 class MoviesCollectionCell: UICollectionViewCell {
    let movieImage: UIImageView = {
        let image = UIImageView()
        image.translatesAutoresizingMaskIntoConstraints = false
        image.clipsToBounds = true
        image.contentMode = .scaleAspectFill
        image.layer.cornerRadius = 10
//        image.image = UIImage(named: "105")
        return image
    }()
    
    let btnRate: UIImageView = {
        let image = UIImageView()
        image.translatesAutoresizingMaskIntoConstraints = false
        image.image = UIImage(named: "goodRate")
        image.clipsToBounds = true
        image.contentMode = .scaleAspectFit
        image.alpha = 0
        return image
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        contentView.addSubview(movieImage)
        movieImage.addSubview(btnRate)
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        movieImage.image = nil
    }
    
    func configure(with urlString: String){
        
        movieImage.sd_setImage(with: URL(string: urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        NSLayoutConstraint.activate([
            movieImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
            movieImage.topAnchor.constraint(equalTo: contentView.topAnchor),
            movieImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
            movieImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            
            btnRate.centerXAnchor.constraint(equalTo: movieImage.centerXAnchor),
            btnRate.centerYAnchor.constraint(equalTo: movieImage.centerYAnchor),
            btnRate.widthAnchor.constraint(equalToConstant: 30),
            btnRate.heightAnchor.constraint(equalToConstant: 30)
        ])
    }
    
func loadImageAfterClickingCell() {
    print("came here")
    btnRate.alpha = 1
    btnRate.image = UIImage(named: "goodRate")
}
}

And now in my didSelectItemAt

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
    cell.loadImageAfterClickingCell()
    
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
    var movieTitle = [String]()
    for obj in moviesArray {
        if let movieObj = obj as? NSDictionary {
               let packShotObj = movieObj.value(forKey: "packShot") as! NSDictionary?
               let id = movieObj.value(forKey: "id") as! String?
               let title = movieObj.value(forKey: "title") as! String?
               if let packShots = packShotObj{
                    let thumbs = packShots.value(forKey: "thumbnail") as! String?
                    if let thumbnails = thumbs{
                        movieThumbnails.append(thumbnails)
                        movieTitle.append(title ?? "")
                    }
                }
        }
    }
    let imageUrl = movieThumbnails[indexPath.row]
    cell.movieImage.image = nil
    cell.configure(with: imageUrl)
    
    
    return cell
}

CodePudding user response:

in your MoviesCollectionCell file put function like this

func loadImageAfterClickingCell() { 
    // TODO: check this cell is already clicked and already download the image like if yourImageView == nil or not nil so you can add guard like guard yourImageView.image == nil else { return } similar to this
    guard let preparedUrl = URL(string: urlString) else { return } 
    yourImageView.alpha = 1
    yourImageView.sd_setImage(with: preparedUrl, placeholderImage: UIImage(named: "ImagePlaceholder"))
}

And after that in didSelectItemAt

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMoviesCellID, for: indexPath) as! MoviesCollectionCell
    cell.loadImageAfterClickingCell()
}

A simple method injection like this must save you as you want.

CodePudding user response:

First note... setup your constraints in init -- Absolutely NOT in layoutSubviews().

You can show/hide your "btnRate" image view much easier by implementing override var isSelected: Bool in your cell class.

It will look like this:

override var isSelected: Bool {
    didSet {
        btnRate.alpha = isSelected ? 1.0 : 0.0
    }
}

So, here's a modified version of your cell class:

class MoviesCollectionCell: UICollectionViewCell {
    let movieImage: UIImageView = {
        let image = UIImageView()
        image.translatesAutoresizingMaskIntoConstraints = false
        image.clipsToBounds = true
        image.contentMode = .scaleAspectFill
        image.layer.cornerRadius = 10
        //        image.image = UIImage(named: "105")
        return image
    }()
    
    let btnRate: UIImageView = {
        let image = UIImageView()
        image.translatesAutoresizingMaskIntoConstraints = false
        image.image = UIImage(named: "goodRate")
        image.clipsToBounds = true
        image.contentMode = .scaleAspectFit
        image.alpha = 0
        return image
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        contentView.addSubview(movieImage)
        movieImage.addSubview(btnRate)
        
        // setup constriaints here
        NSLayoutConstraint.activate([
            movieImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10),
            movieImage.topAnchor.constraint(equalTo: contentView.topAnchor),
            movieImage.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10),
            movieImage.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
            
            btnRate.centerXAnchor.constraint(equalTo: movieImage.centerXAnchor),
            btnRate.centerYAnchor.constraint(equalTo: movieImage.centerYAnchor),
            btnRate.widthAnchor.constraint(equalToConstant: 30),
            btnRate.heightAnchor.constraint(equalToConstant: 30)
        ])

        // I don't have your "goodRate" image, so
        //  let's set a SF Symbol as the btnRate image
        if let img = UIImage(systemName: "hand.thumbsup") {
            btnRate.image = img
        }
        btnRate.tintColor = .white
        btnRate.layer.shadowColor = UIColor.black.cgColor
        btnRate.layer.shadowOffset = CGSize(width: 1.0, height: 2.0)
        btnRate.layer.shadowRadius = 2
        btnRate.layer.shadowOpacity = 0.8
        btnRate.layer.masksToBounds = false
    }
    
    override var isSelected: Bool {
        didSet {
            btnRate.alpha = isSelected ? 1.0 : 0.0
        }
    }

    override func prepareForReuse() {
        super.prepareForReuse()
        movieImage.image = nil
    }
    
    func configure(with urlString: String){
        
        //movieImage.sd_setImage(with: URL(string: urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

and a quick example controller:

class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate {
    
    var collectionView: UICollectionView!
    
    let colors: [UIColor] = [
        .systemRed, .systemGreen, .systemBlue,
        .cyan, .magenta, .yellow,
    ]
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let fl = UICollectionViewFlowLayout()
        fl.itemSize = CGSize(width: 100.0, height: 100.0)
        fl.scrollDirection = .vertical
        fl.minimumLineSpacing = 8
        fl.minimumInteritemSpacing = 8
        
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: fl)
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(collectionView)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
            collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
            collectionView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
        ])
        
        collectionView.register(MoviesCollectionCell.self, forCellWithReuseIdentifier: "c")
        collectionView.dataSource = self
        collectionView.delegate = self
        
    }
    
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return 40
    }
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let c = collectionView.dequeueReusableCell(withReuseIdentifier: "c", for: indexPath) as! MoviesCollectionCell
        
        // I don't have your cell images, so let's just cycle through some colors
        //  for the movieImage background
        c.movieImage.backgroundColor = colors[indexPath.item % colors.count]
        
        return c
    }
    
}

It looks like this before selecting any cell:

enter image description here

and after selecting a cell:

enter image description here

You will notice that you can scroll up and down through the cells, and the "selected" state of the selected cell is maintained -- without any additional code.

  • Related