C. Albert C. Albert - 4 months ago 14
Swift Question

Swift - Calling a func of a UIViewController from a Custom UITableViewCell

Here is my problem:

I have a MainTableViewController with an outlet for a table that uses custom UITableViewCells. I also have an outlet for a UIView, called BlackView, in MainTableViewController.

What I want to do: Inside myCustomCell I would like to set "BlackView.hidden = false". I am trying to use "class func" in my MainTableViewController file, and call it from myCustomCell, but it is not working, because Xcode stops recognizing BlackView when I put the word "class" before "func".

So, I would like to call a function of a MainTableViewController or access its outlet from the .swift of my .xib file.

Does anybody knows how to do that?

Here is my .xib file:

My .xib file

Here is the .swift for my .xib file:

class myCustomCell: UITableViewCell {

@IBOutlet weak var commentTextView: UITextView!


override func awakeFromNib() {

commentTextView.delegate = self

super.awakeFromNib()

}


func textViewDidBeginEditing(textView: UITextView) {

MainTableViewController.hideBlackView(true)

}

func textViewDidEndEditing(textView: UITextView) {

var comment = commentTextView.text

}

}


Here is my MainTableViewController:

class MainTableViewController: UIViewController

@IBOutlet weak var MyTable: UITableView!

@IBOutlet weak var BlackView: UIView!

override func viewDidLoad() {

BlackView.hidden = true;

MyTable.registerNib(UINib(nibName: "myCustomCell", bundle: nil), forCellReuseIdentifier: "myCustomCellID")

}

class func hideBlackView(setToHidden: Bool) {

if setToHidden == true {

BlackView.hidden = true

} else {

BlackView.hidden = false

}


}


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


let cell = tableView.dequeueReusableCellWithIdentifier("myCustomCellID") as! PublishHeaderTableViewCell

cell.selectionStyle = UITableViewCellSelectionStyle.None


return cell


}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return 1

}

}


Here is my Main.storyboard:

My Main.storyboard

Answer

The Answer is Delegation

BlackView is an instance which will be created by the OS. The outlet is a special property (called an outlet) referencing that instance. When MainTableViewController is shown, an instance of it is created by the OS.

You probably want to use an instance method, not a class method to change the hidden property on instance of BlackView. To do that you will need to pass a reference of the MainTableViewController instance to myCustomCell. This is called delegation, which is how ios programming and most MVC models work.

To do this add define a delegate protocol (just above the definition for the custom cell would be normal) and add a weak var to the cell of this type:

    // use a class protocol for delegates so weak properties can be used
    protocol MyCustomCellDelegate: class {
        func hideBlackView(setToHidden: Bool)
    }

    class MyCustomCell: UITableViewCell {

            weak var delegate: MyCustomCellDelegate?
            @IBOutlet weak var commentTextView: UITextView!

            override func awakeFromNib() {

                commentTextView.delegate = self

                super.awakeFromNib()
            }


            func textViewDidBeginEditing(textView: UITextView) {

                delegate?.hideBlackView(true)
            }

            func textViewDidEndEditing(textView: UITextView) {

                var comment = commentTextView.text
            }
    }

Then when you are setting up the cells in cellForRowAtIndexPath, cast as the the proper cell type which should be MyCustomCell in the example you've given not PublishHeaderTableViewCell (also note that I've switched your custom cell class name to starting with a capital letter as is industry standard in ios development). Finally, set the delegate to the instance of the MainTableViewController (which is called "self" from within instance functions).

BTW, in your case, you are only using one cell, so you probably don't need to dequeue and reuse cells. You could just take all that out and return a simple instance you created of the cell in the cellForRowAtIndexPath method. Anyway, I will leave all that in place in case you have just simplified your code for Stack Overflow.

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

            // you need to cast the cell to your custom class to use it
            let cell = tableView.dequeueReusableCellWithIdentifier("myCustomCellID") as! MyCustomCell

            cell.selectionStyle = UITableViewCellSelectionStyle.None

            // set the delegate
            cell.delegate = self

            return cell
    }

Finally, and VERY IMPORTANTLY, you need to declare that MainTableViewController conforms to the the protocols that will use it so the functions (methods) that other objects want to delegate to it will succeed. In your case it needs to conform to both MyCustomCellDelegate which we wrote above, but also since you are using it for the for the tableView's data source (for cellForRowAtIndexPath and numberOfRowsInSection) you need to declare that it conforms to UITableViewDataSource (You may have done this already through Interface Builder (story board).. if not you can do it in the class definition).

    // Declare objects conform to protocols by including protocol names separated by commas after the colon (or the class inherited from) 
    class MainTableViewController: UIViewController, MyCustomCellDelegate, UITableViewDataSource {

            @IBOutlet weak var MyTable: UITableView!
            @IBOutlet weak var BlackView: UIView!

            override func viewDidLoad() {
                BlackView.hidden = true
                MyTable.registerNib(UINib(nibName: "myCustomCell", bundle: nil), forCellReuseIdentifier: "myCustomCellID")
            }

        func hideBlackView(setToHidden: Bool) {
                    // since they are both bools just set BlackView.hidden to the setToHidden parameter directly
                    BlackView.hidden = setToHidden
            }


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

                    let cell = tableView.dequeueReusableCellWithIdentifier("myCustomCellID") as! MyCustomCell
                    cell.selectionStyle = UITableViewCellSelectionStyle.None

                    // set the delegate
                    cell.delegate = self

                    return cell
            }

            func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
                return 1
            }
    }

As a final note, I'm not sure is setting the delegate for the UITextView is a appropriate in the awakeFromNib method in your custom cell. I know that this method doesn't always fire.. In your case since it is on an outlet, I think it is OK, but I don't use XIB files very much my self, so you may want to print to the console to make sure it is being called each time or research the issue more.