12

How can I add a Gesture Recognizer to a UIImageView in a table cell? I want it so that if a user taps an image in the cell, the image will change and the data model will update.

I know this needs to be set up in the UITableViewController. My code currently can execute a command if anywhere in the cell is tapped, but I would like it to execute only if the image is tapped, not anywhere in the cell.

I setup up the gesture recognizer in viewDidLoad

override func viewDidLoad() {
    super.viewDidLoad()

    // Load sample data
    loadSampleHabits()

    // Initialize tap gesture recognizer
    var recognizer = UITapGestureRecognizer(target: self, action: #selector(tapEdit(recognizer:)))
    // Add gesture recognizer to the view
    self.tableView.addGestureRecognizer(recognizer)

And this is the function

//action method for gesture recognizer
func tapEdit(recognizer: UITapGestureRecognizer) {
    if recognizer.state == UIGestureRecognizerState.ended {
        let tapLocation = recognizer.location(in: self.tableView)
        if let tapIndexPath = self.tableView.indexPathForRow(at: tapLocation) {
            if let tappedCell = self.tableView.cellForRow(at: tapIndexPath) as? HabitTableViewCell {
                print("Row Selected")

            }
        }
    }

What the cell looks like

As a secondary question, are there any conflicts if I want to add a gesture recognizer to the cell and the image view within the cell?

Rob
  • 415,655
  • 72
  • 787
  • 1,044
b.gibson
  • 121
  • 1
  • 5
  • You can try removing if recognizer.state == UIGestureRecognizerState.ended condition from your method. Check my ans. – luckyShubhra Jul 12 '17 at 05:46
  • @b.gibson: I would adapt to luckyShubhra's answer. I also used the same somewhere in one of my projects and did not face any issue – Lohith Korupolu Jul 12 '17 at 06:22
  • If you're going to use `UITapGestureRecognizer` on image view, you don't need to check the `state`. That's really only relevant for continuous gestures (like long press or pan gestures). But just using a button with an image (like [iOS Dev suggested](https://stackoverflow.com/a/45049086/1271826)) makes a lot more sense to me. – Rob Jul 12 '17 at 07:24

6 Answers6

17

You are adding gesture recognizer on your tableview instead of imageView as you required. Yo need to move your code from viewDidLoad to cellForRowAtIndexPath and add gesture to imageView in each cell while configuing your cell.

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
     var recognizer = UITapGestureRecognizer(target: self, action: #selector(tapEdit(recognizer:)))
     // Add gesture recognizer to your image view
     cell.yourimageview.addGestureRecognizer(recognizer)
}

Note: Do make sure to enable userinteraction of your image view

cell.yourimageview.userInteractionEnabled = YES;

For your requirement I will suggest using UILongPressGestureRecognizer as it has less chances of conflict in gesture and didselect. Yo can add UILongPressGestureRecognizer in viewDidLoad and access it as per your requirement.

let lpgr = UILongPressGestureRecognizer(target: self, action: #selector(ViewController.handleLongPress(_:)))
lpgr.minimumPressDuration = 1
tableView.addGestureRecognizer(lpgr)

Define method as

func handleLongPress(_ gesture: UILongPressGestureRecognizer){
if gesture.state != .began { return }
let tapLocation = gesture.location(in: self.tableView)
    if let tapIndexPath = self.tableView.indexPathForRow(at: tapLocation) {
        if let tappedCell = self.tableView.cellForRow(at: tapIndexPath) as? HabitTableViewCell {
            print("Row Selected")

        }
}

You can try removing if recognizer.state == UIGestureRecognizerState.ended condition from your method.

UITapGestureRecognizer is a discrete gesture, and as such, your event handler is called only once when the gesture was recognized. You don't have to check the state at all. Certainly you won't receive a call for the state of .Began. For more info consider @Rob ans here.

luckyShubhra
  • 2,731
  • 1
  • 12
  • 19
  • was in the similar position to add a tap like gesture on UIimage in a cell, a good hack is to minimise the `lpgr.minimumPressDuration = 0.01` to replicate a tap – Frostmourne Apr 04 '20 at 11:41
  • It works very well ! I create a Radio Button Group, that swift eliminate for no reason ! I added a tap ( two different for each image (circle and largecircle.fill.circle) in two UIImageView. The #selector function interchange the image of the two UIImageView and update the NSManagedObject linked to the state of the Radio Button Group. the function : func tapEdit(recognizer: UITapGestureRecognizer) did the trick! They use the same function that swap the state of the buttons. Thanks. – araferna Jun 19 '20 at 23:11
4

Add This line in cell for row at index path

     var recognizer = UITapGestureRecognizer(target: self, action: #selector(tapEdit(recognizer:)))
        // Add gesture recognizer to the view
        cell.yourimageviewname.addGestureRecognizer(recognizer)

cell.yourimageviewname.userInteractionEnabled = true;
BHAVIK
  • 890
  • 7
  • 35
3

For my suggestion you have to use UIButton in cell, for performance improvements,

UIButtons

Specially designed for this and have been extensively optimized by Apple for touches.

If you want image in cell you can use UIButton with Image inside.

Bhavesh Dhaduk
  • 1,888
  • 15
  • 30
3

I have had design a solution like this. I just write a sample code below:

import UIKit

protocol CellImageTapDelegate {
    func tableCell(didClickedImageOf tableCell: UITableViewCell)
}

class SampleCell : UITableViewCell {

    var delegate : CellImageTapDelegate?
    var tapGestureRecognizer = UITapGestureRecognizer()

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initialize()
    }

    private func initialize() {
        tapGestureRecognizer.addTarget(self, action: #selector(SampleCell.imageTapped(gestureRecgonizer:)))
        self.addGestureRecognizer(tapGestureRecognizer)
    }

    func imageTapped(gestureRecgonizer: UITapGestureRecognizer) {
        delegate?.tableCell(didClickedImageOf: self)
    }
}

class ViewController: UITableViewController, CellImageTapDelegate {

    // CellImageTapDelegate
    func tableCell(didClickedImageOf tableCell: UITableViewCell) {
        if let rowIndexPath = tableView.indexPath(for: tableCell) {
            print("Row Selected of indexPath: \(rowIndexPath)")
        }
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 10
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "SampleCellID", for: indexPath) as! SampleCell
        cell.delegate = self
        return cell
    }
}

remember to do following in storyboard 1. enable user interaction of imageview 1. enable user interaction of imageview 2. set class of tableviewcell 2. set class of tableviewcell 3. set reuse identifier of tableviewcell
3. set reuse identifier of tableviewcell

Codus
  • 1,433
  • 1
  • 14
  • 18
  • tested project for this solution. [zip project on google drive](https://drive.google.com/open?id=0B70RpDc-VWUuSVA0NEFnRldfSE0) – Codus Jul 12 '17 at 05:51
  • 1
    It only detects if the cell is clicked, but the gesture recognizer is not attached to any control, for example an UIImageView or a UIButton . – bibscy Mar 22 '19 at 18:41
3
// create an instance of UITapGestureRecognizer and tell it to run 
// an action we'll call "handleTap:"
let tap = UITapGestureRecognizer(target: self, action: Selector("handleTap:"))
// we use our delegate
tap.delegate = self
// allow for user interaction
cell.imageViewName.userInteractionEnabled = true
// add tap as a gestureRecognizer to tapView
cell.imageViewName.addGestureRecognizer(tap)
selva raj
  • 154
  • 5
1
import UIKit

class UserInfoCell: UITableViewCell{

    @IBOutlet weak var imagePlaceholder: UIImageView!
}

class ViewController: UIViewController,UITableViewDelegate,UITableViewDataSource,UIImagePickerControllerDelegate,UINavigationControllerDelegate {

    @IBOutlet weak var tableView: UITableView!


    let imagePicker = UIImagePickerController()
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return 1


    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

            let cell = tableView.dequeueReusableCell(withIdentifier: "UserInfoCell" ,for: indexPath ) as! UserInfoCell
            let recognizer = UITapGestureRecognizer(target: self, action: #selector(self.openGallery))

            cell.imagePlaceholder.addGestureRecognizer(recognizer)
            recognizer.numberOfTapsRequired = 1
            cell.imagePlaceholder.isUserInteractionEnabled = true
            cell.name.text = "Akshay"

            if let data = UserDefaults.standard.data(forKey: "savedImage") {
                cell.imagePlaceholder.image = UIImage(data: data as Data)
            }

            return cell




    }
    @objc func openGallery(){
        imagePicker.sourceType = .photoLibrary
        present(imagePicker,animated:  true, completion: nil)
    }
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {

        let userimage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
        let imageData = userimage.jpegData(compressionQuality: 1)!
        UserDefaults.standard.setValue(imageData, forKey: "savedImage")
        print("image found")
        self.imagePicker.dismiss(animated: true, completion: nil)

        self.tableView.reloadData()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        imagePicker.delegate = self
tableView.tableFooterView = UIView()

    }
}

This code select image from gallery using Tapgesture of ImageView inside a TableViewCell

Ravi Sachaniya
  • 1,641
  • 18
  • 20