4

Been trying to figure this out for a while now and after a couple hours of searching for a solution I decided it was time to ask.

I have a tableview that gets populated by custom UITableViewCells and currently when you tap on a cell it takes you to a detail view.

Within the custom cell there is an image, I would like the user to be able to tap on that image and segue to a popover VC that shows the image.

What I'm having trouble with is creating the segue when the image is tapped.

In the file for the custom cell, I've set up a tap gesture recognizer on the image (pTap):

override func awakeFromNib() {
    super.awakeFromNib()

    let tap = UITapGestureRecognizer(target: self, action: #selector(PostCell.voteTapped(_:)))
    let ptap = UITapGestureRecognizer(target: self, action: #selector(PostCell.imageTapped(_:)))
    tap.numberOfTapsRequired = 1
    ptap.numberOfTapsRequired = 1
    voteImage.addGestureRecognizer(tap)
    voteImage.userInteractionEnabled = true
    featuredImg.addGestureRecognizer(ptap)
    featuredImg.userInteractionEnabled = true


}

I also have a function in the custom cell file for the tap:

func imageTapped(sender: UIGestureRecognizer) {

  print("image tapped")


}

In my view controller file I've added a segue in did select row at index path:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

    let post: Post!

    if inSearchMode {
        post = filteredVenues[indexPath.row]
    } else {

        post = posts[indexPath.row]
    }

    print(post?.venueName)

    performSegueWithIdentifier("imageTapped", sender: nil)



    performSegueWithIdentifier("DetailsVC", sender: post)

}

Also, in the storyboard I have created a segue from the VC that holds the tableview with the custom cells to the VC I'd like to show when the image is tapped.

I've tried several different methods of getting this to work and haven't had any luck, the code you see above are what remains after my many failed attempts. I feel that the function for the tap in the custom cell file and the segue in the VC file are a part of the solution so that is why I have left them in.

Any help would be appreciated. Thanks!

Updates to code from answers below:

Added protocol

protocol ImageSegueProtocol: class {
    func imageTapped(row: Int)
}


class PostCell: UITableViewCell {

Added IAB Func

@IBAction func imageTapped(sender: UIGestureRecognizer) {
   guard let row = row else { return }
    delegate?.imageTapped(row)
    print("image tapped func")
}

Declared delegate in the Main VC

weak var delegate:postCell?

Assigned Delgate

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    //let post = posts[indexPath.row]

    if let cell = tableView.dequeueReusableCellWithIdentifier("PostCell") as? PostCell {

        var img: UIImage?
        var vImg: UIImage?
        postCell?.delegate = self

Added extension function

extension FeedVC: ImageSegueProtocol {

func imageTapped(row: Int) {
    if inSearchMode == true {
        let object = filteredVenues[row]
        performSegueWithIdentifier("imageTapped", sender: object)
        print("ext func")
    } else {
        let object = posts[row]
        performSegueWithIdentifier("imageTapped", sender: object)
        print("ext func")
    }



}
ryanbilak
  • 383
  • 1
  • 2
  • 13

3 Answers3

5

You could do it like this:

Within the cell file, declare a protocol at the top, and then set up some properties within the cell class itself and the delegate behaviour in response to the image being tapped:

protocol SomeCellProtocol: class {
    func imageTapped(row: Int)
}


class SomeCell: UITableViewCell {

    weak var delegate: SomeCellProtocol?
    var row: Int?

    @IBAction func imageTapped() {
        guard let row = row else { return }
        delegate?.imageTapped(row)
    }
}

You must then make your ViewController adopt SomeCellProtocol and call the segue within like so:

extension SomeViewController: SomeCellProtocol {

    func imageTapped(row: Int) {
        //get object from array and call segue here
    }
}

And you must set your ViewController as the delegate of your cells by calling:

someCell.delegate = self

and pass the row to the cell:

someCell.row = indexPath.row

within your cellForRowAtIndexPath method of the ViewController.

So, when the button is tapped within the cell (or you can do it with a GestureRecognizer on the ImageView if you want) it will force the delegate (the ViewController) to call its imageTapped function, passing a row parameter which you can use to determine which object in the table (its corresponding data array) should be passed via the Segue.

RossP
  • 434
  • 8
  • 10
  • Thanks Ross, can you help me with getting the object from the array in the VC extension? Just need some explanation on what exactly you mean – ryanbilak Jul 31 '16 at 12:50
  • Basically you need to know which object you want to show details of. You passed the row to the cell, and it gives you the row back in the delegate method, so just use that row to get the object from your array: let object = filteredVenues[row] if you're in search mode or let object = posts[row] if not! – RossP Jul 31 '16 at 12:53
  • 1
    Thanks would that look like this? `extension FeedVC: ImageSegueProtocol { func imageTapped(row: Int) { if inSearchMode == true { let object = filteredVenues[row] performSegueWithIdentifier("imageTapped", sender: nil) } else { let object = posts[row] performSegueWithIdentifier("imageTapped", sender: nil) } } }` And would the delegate be: `PostCell().delegate = self` PostCell is the file for my custom cells – ryanbilak Jul 31 '16 at 12:59
  • make the sender the object and then in performSegueWithIdentifier you will need to get the sender and pass it to the destinationViewController. Setting the delegate would be done by postCell.delegate = self within cellForRowAtIndexPath – RossP Jul 31 '16 at 13:11
  • Hey Ross, still having some trouble. Here's what I did: `extension FeedVC: ImageSegueProtocol { func imageTapped(row: Int) { if inSearchMode == true { let object = filteredVenues[row] performSegueWithIdentifier("imageTapped", sender: object) print("ext func") } else { let object = posts[row] performSegueWithIdentifier("imageTapped", sender: object) print("ext func") } }` Also confused on how making the delegate `postCell.delegate = self` will work since I don't have `postCell` anywhere – ryanbilak Jul 31 '16 at 14:03
  • Also the IAB Func needs sender: UIGestureRecognizer correct? I added a print line to this function as well and it doesn't appear in the console when I tap the image – ryanbilak Jul 31 '16 at 14:11
  • sorry it should be cell.delegate = self you also need to call cell.row = indexPath.row You are getting gesture recognisers confused with IBActions - do a little reading around the 2 and decide which you will use! Personally I would use a gestureRecognizer which will call a selector as you have above - then just have a func called imageTapped() inside which you will call the delegate method as I showed you above – RossP Jul 31 '16 at 14:37
  • Definitely would like to use the gesture recognizer instead of creating a button. Do you know of any resources or sample code that has this implemented? I feel like if I can just see the finished product everything will click for me – ryanbilak Jul 31 '16 at 20:56
  • Thanks for answer! Helped me! – Vlad Pulichev May 18 '17 at 20:42
0

What you need to do is to create a button instead of the image and this button will hold the actual image:

enter image description here

The rest is easy, ctrl drag an IBAction into your custom cell file. Now you need to communicate with your View Controller in order to invoke your segue method.

You can achieve that using 2 design patterns:

  1. Using post notification:

    Inside your View Controller add an observer:

    NSNotificationCenter.defaultCenter().addObserver(self,
                              selector: #selector(YourViewController.cellSelected(_:)),
                              name: "CellSelectedNotificationName",
                              object: nil)
    

    Your method cellSelected inside your View Controller should look like this:

    func cellSelected(notification : NSNotification)
    {
      if let passedObject = notification.object as? YourCustomObject
      {
       // Do what ever you need with your passed object
       // When you're done, you can invoke performeSegue that will call prepareForSegue
       // When invoking performeSegue you can pass your custom object
      }
     }
    

    Inside your CustomCell class at the IBAction method of your button:

    @IBAction func buttonTapped()
    {
      // Prepare the object you want to pass...
      NSNotificationCenter.defaultCenter().postNotificationName("CellSelectedNotificationName", object: YourCustomObjectYouWantToPass)
     } 
    

    In a nut shell, a button inside a cell was tapped, you created an object inside that cell and you pass it to a View Controller using the notification centre. Now, you can segue with your custom object.

NOTE: If you don't need to pass an object you can basically ctrl + drag from your UIButton (at your storyboard) to another View Controller.

  1. Using a delegate that is pointing to the View Controller.

Good luck.

Community
  • 1
  • 1
OhadM
  • 4,687
  • 1
  • 47
  • 57
  • Thanks Ohad, I will try this out. Can you give me some information about the delegate that needs to point to the VC? – ryanbilak Jul 31 '16 at 11:53
  • I have added links for each design, take a look at them. If you still won't get it, let me know I'll add an example. – OhadM Jul 31 '16 at 11:55
  • Awesome thank you, I'll take a look and get back to you if needed – ryanbilak Jul 31 '16 at 11:58
  • Hey Ohad can you add an example for me? – ryanbilak Jul 31 '16 at 13:27
  • @ryanbilak, I have updated the answer with an example of NSNotificationCenter and another example which is in the *NOTE* section. If you need a delegate example let me know. – OhadM Aug 01 '16 at 07:17
0

As OhadM said, create a button and set the background image as the image you want displayed. From there, you don't even need an IBAction. Control-drag from the button to the next view controller and create the segue.

Then, if you want to do any setup before the segue, in the first VC you'd have something like:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "imageButtonPressed" { // Make sure you name the segue to match

            let controller = segue.destinationViewController as! SecondVC
            controller.someText = "Hi"
            controller.someInt = 5
        }
}
Shades
  • 5,568
  • 7
  • 30
  • 48