How to reference UITableViewController from a UITableViewCell class?

34,807

Solution 1

I wouldn't create this kind of dependency between the cell and the view controller - that makes the architecture more intricate and the cell not reusable.

I suggest you to use the delegation pattern, which may sound a little complicated - although you're already using (UITableViewDelegate is a typical example):

  • create a protocol MyCellProtocol with one method didTapCell, accepting a UITableViewCell and/or some custom data you want to pass to the view controller
  • create a public delegate property in your custom cell: weak var cellDelegate: MyCellProtocol?
  • in the didTapXXX handler or didSelectRowAtIndexPath of your cell, call self.cellDelegate?.didTapCell(), passing the expected parameters
  • in your view controller, implement the MyCellProtocol
  • in cellForRowAtIndexPath of your view controller, when creating/dequeuing the cell, set its cellDelegate property to self

At this point, when a tap is done in your cell, the didTapCell method of the view controller is called, and from there you can do whatever you need to achieve.

The key point is: rather than making the cell handle the cell tap/selection, notify the view controller and let it do the job.

Solution 2

extension UIView {
    var parentViewController: UIViewController? {
        var parentResponder: UIResponder? = self
        while parentResponder != nil {
            parentResponder = parentResponder!.next
            if let viewController = parentResponder as? UIViewController {
                return viewController
            }
        }
        return nil
    }
}

Inside your cell

if let myViewController = parentViewController as? MyViewController {
    print(myViewController.title)
}

Solution 3

I don't think you should do that and probably you're doing something wrong but if you really-really need it then just have a property in your CustomCell class.

weak var viewController : UIViewController

then when you create the cell in cellForRowAtIndexPath set the property

cell.viewController = self

after that you can easily access the view controller from within the cell code:

self.viewController.doSomething()

But again, in my opinion, you should redesign your code. The cell should not care about the controller. It should care only about displaying itself and nothing else

Solution 4

add Extension

 extension UITableViewCell {

        var viewControllerForTableView : UIViewController?{
            return ((self.superview as? UITableView)?.delegate as? UIViewController)
        }

    }

now downCast as below

   if let viewController = self.viewControllerForTableView as? AnyController{

       print(viewController.variable)

   } 

.

Solution 5

Best way is to implement Delegation. Your delegate will inform the view controller about the button click. And, In turn your view controller (who is conforming the protocol for above delegate) will handle the task that you want to perform on click event. (Delegation pattern tutorials are available online. you can go through them if you want to know how to do delegation).

Share:
34,807
Norly Canarias
Author by

Norly Canarias

Updated on February 01, 2020

Comments

  • Norly Canarias
    Norly Canarias over 4 years

    I have the following:

    1. UITableViewController
    2. UITableView
    3. Custom UITableViewCell subclass

    I used a .xib file for the cell, which is a CustomCell subclass. This custom cell handles IBActions for some touch events on the cell's buttons.

    I'd like to reference the ViewController and some of its variables from the cell.

    How am I supposed to access the UITableViewController from the UITableViewCell class?

  • Norly Canarias
    Norly Canarias over 9 years
    > "The cell should not care about the controller. It should care only about displaying itself and nothing else" Does it mean that it is also bad practice to add buttons in a custom cell if the button's action would be to access or change some model property? Because in that case the model can accessed only from the controller since it is the source of the table view...
  • Andrey Chernukha
    Andrey Chernukha over 9 years
    it is not of course. a bad practice would be to have code which processes a lot of model's stuff in the button's action. a good practice in this case is to have just one line of code in the button's action. the line should post a notification or call a delegate method. and then the controller should react on this call or this notification and run all the needed code. controller, not the cell. i hope you understand what i'm talking about
  • Fareed Alnamrouti
    Fareed Alnamrouti about 9 years
    in most UI frameworks its very common to access the container of the object, for example in ActionScript you can call the parent using this.parent, and from my experience in Apple language it seems Apple is not experienced enough with visual programming language, so i guess this solution is very acceptable and clever and i will give it +1
  • zeeple
    zeeple over 8 years
    The second bullet above appears to be for Objective-C. In swift one might do this: let delegate: ClasThatImplementsProtocol
  • Sebastian Boldt
    Sebastian Boldt about 8 years
    This may work. But when encapsulating the dataSource to a different class, as you should (dont put the DS into the controller), you need to find a different way of connecting cells and controller. One thing you could do is to use the tableView delegate method willDisplayCell to assign the tableview as a delegate to the cell. You then need to unlink them in prepareForReuse and dealloc or deinit depending on which language you are using. Notifications also not the ideal solution if there multiple controllers active that listen to the same notification.
  • Mohammad Hadi
    Mohammad Hadi over 7 years
    thank you very much, I know it 's spam but you save my day ;) good luck
  • webjunkie
    webjunkie over 7 years
    I wanted to perform a segue from a uicollectionviewcell nested inside a uitableviewcell and this worked perfectly!. Thanks.
  • Jack Robson
    Jack Robson over 7 years
    Worked in the Master Detail project template. Thank you.
  • MBH
    MBH about 7 years
    it would be great if you provide some code in the answer :) @Antonio
  • Sean
    Sean over 6 years
    Just got to say thank you so much. Sorted me out. If anyone would like more info on Delegates and Protocols watch this great short video: youtube.com/watch?v=DBWu6TnhLeY
  • troligtvis
    troligtvis over 6 years
    in Swift 4, .nextResponder() is changed to .next
  • Ivan
    Ivan over 5 years
    Won't it create a reference cycle like: controller -> collection view -> cell -> controller?
  • Antonio
    Antonio over 5 years
    Thanks @Ivan, yes you're right, the delegate should be weak, just updated the answer.
  • Muhammed Gül
    Muhammed Gül over 4 years
    I think defining the ViewController as weak inside cell might be better.
  • Andrey Chernukha
    Andrey Chernukha over 4 years
    @MuhammedGül absolutely true. Thank you for pointing this out