iosswiftuicollectionviewcelluilongpressgesturerecogni

How to detect long press on a UICollectionViewCell and and perform an action a the cell


I have a list of images displaying in a UICollectionViewCell. Now I want a way to display an overlay on the image when user long press on the cell. I have been able to place a long press gesture on the cell but unfortunately how to perform the overlay on the cell is where I'm struggling to achieve.

In my cellForItemAt I have this

let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reUseMyCellID, for: indexPath) as! MyCollectionCell
cell.gestureRecognizers?.removeAll()
        cell.tag = indexPath.row
        let directFullPreviewer = UILongPressGestureRecognizer(target: MyCollectionCell(), action: #selector(MyCollectionCell().directFullPreviewLongPressAction))
        cell.addGestureRecognizer(directFullPreviewer)

I have this function for the action on LongPressGestureRecognizer in my MyCollectionCell

class MyCollectionCell: UICollectionViewCell {
    
    weak var textLabel: UILabel!
    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 movieOverlay: UIView = {
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.backgroundColor = .black.withAlphaComponent(0.7)
        view.alpha = 0
        return view
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        contentView.addSubview(movieImage)
        movieImage.addSubview(btnRate)
        movieImage.addSubview(movieOverlay)
        
        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),
            
            movieOverlay.leadingAnchor.constraint(equalTo: movieImage.leadingAnchor),
            movieOverlay.topAnchor.constraint(equalTo: movieImage.topAnchor),
            movieOverlay.trailingAnchor.constraint(equalTo: movieImage.trailingAnchor),
            movieOverlay.bottomAnchor.constraint(equalTo: movieImage.bottomAnchor)
        ])
        
    }
    
    override func prepareForReuse() {
        super.prepareForReuse()
        movieImage.image = nil
    }
    
    func configure(with urlString: String){

        movieImage.sd_setImage(with: URL(string: urlString), placeholderImage: UIImage(named: "ImagePlaceholder"))
        
    }
    
    @objc func directFullPreviewLongPressAction(g: UILongPressGestureRecognizer)
    {
        if g.state == UIGestureRecognizer.State.began
        {
            
            movieOverlay.alpha = 1
        }
    }
   
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
}

Solution

  • Your current code has many issues when creating the long press gesture.

    You are setting the target to a new cell instance that is immediately thrown away. Set the target as cell, not MyCollectionCell().

    You are also using the wrong syntax for the selector. Don't attempt to create a new instance of a cell. Just pass the name of the method #selector(MyCollectionCell.directFullPreviewLongPressAction).

    Having said all of that, there is no reason this code should be in the cellForItemAt method. You should be creating the long press gesture inside the cell class.

    Remove these three lines from cellForItemAt:

    cell.gestureRecognizers?.removeAll()
    let directFullPreviewer = UILongPressGestureRecognizer(target: MyCollectionCell(), action: #selector(MyCollectionCell().directFullPreviewLongPressAction))
    cell.addGestureRecognizer(directFullPreviewer)
    

    Then add the following lines inside the init of MyCollectionCell:

    let directFullPreviewer = UILongPressGestureRecognizer(target: self, action: #selector(directFullPreviewLongPressAction))
    addGestureRecognizer(directFullPreviewer)
    

    Now the cell is fully responsible for setting up and handling the long press gesture.


    Unrelated to your question, you should know that the line:

    cell.tag = indexPath.row
    

    should be:

    cell.tag = indexPath.item
    

    row is used for UITableView. item is used for UICollectionView.

    But besides that, you really should avoid such code. If your collection view allows cells to be inserted, deleted, and/or reordered, then a cell's tag will no longer represent the item you set.