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:
and after selecting a cell:
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.