How to push a new view controller when a table cell is tapped?

Shawn Clarke picture Shawn Clarke · Aug 14, 2015 · Viewed 9.5k times · Source

I have created this table with 3 sections and 7 rows. The code is shown below

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

@IBOutlet var subjectTabelView: UITableView!

var slSubject = ["English Lang&Lit", "Chinese Lang&Lit", "Economics"]
var hlSubject = ["Mathematics", "Chemistry", "Biology"]
var tokSubject = ["Theory of Knowledge"]

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    subjectTabelView.dataSource = self
    subjectTabelView.delegate = self
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func numberOfSectionsInTableView(tableView: UITableView) -> Int {
    return 3
}

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    if section == 0{
        return hlSubject.count
    }else if section == 1{
        return slSubject.count
    }else {
        return tokSubject.count
    }

}

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

    let subjectCell = tableView.dequeueReusableCellWithIdentifier("idSubjectCell", forIndexPath: indexPath) as! UITableViewCell

    if indexPath.section == 0 {
        subjectCell.textLabel?.text = hlSubject[indexPath.row]
    } else if indexPath.section == 1{
        subjectCell.textLabel?.text = slSubject[indexPath.row]
    } else {
        subjectCell.textLabel?.text = tokSubject[indexPath.row]
    }

    return subjectCell
}

func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
    if section == 0 {
        return "HL"
    } else if section == 1{
        return "SL"
    } else {
        return "ToK"
    }
}



}

What do I have to do to make every cell in this table pushes a new view controller when it is tapped? The picture of my storyboard is shown below. In my storyboard, my view controller, I have already created a navigation controller, and made the view controller that has the table the rootViewController. And for now, my tableView has only one prototype cell and one cell identifier.

Thank you!

Answer

Hugo Alonso picture Hugo Alonso · Aug 14, 2015

Suppose your "locationVC" is:

class LocationVC: UIViewController {

    @IBOutlet weak var fromWhereLabel: UILabel!
    //This can be changed when creating this UIViewController
    var textToShow : String?

    override func viewWillAppear(animated: Bool) {
        if let textToShow = textToShow {
            fromWhereLabel.text = textToShow
        }
    }
}

then, just adding function below to your code in ViewController named UIViewController (that should have a better name ;-)) you can achieve your goal.

 func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
      //if such cell exists and destination controller (the one to show) exists too..
        if let subjectCell = tableView.cellForRowAtIndexPath(indexPath), let destinationViewController = navigationController?.storyboard?.instantiateViewControllerWithIdentifier("locationVC") as? LocationVC{
            //This is a bonus, I will be showing at destionation controller the same text of the cell from where it comes...
            if let text = subjectCell.textLabel?.text {
                destinationViewController.textToShow = text
            } else {
                destinationViewController.textToShow = "Tapped Cell's textLabel is empty"
            }
          //Then just push the controller into the view hierarchy
          navigationController?.pushViewController(destinationViewController, animated: true)
        }
     }

You will be able to have a LocationVC UIViewController launched every time you tap a cell, and it will have some value to prove it right. :)

Hope it Helps!

UPDATE: Code and Instructions below are for allowing to launch different UIViewControllers after tap on cells

1.- Let's create a class that will be the parent for every one of our new UIViewControllers (the ones we are willing to go from our tableview cell's tap):

public class CommonDataViewController: UIViewController {
//Here we are going to be putting any data we want to share with this view
  var data: AnyObject?
}

2.- Let's create some sort of Navigation rules, just to be organised ;-)

enum Navigation: Int {
    case vc1 = 0, vc2 = 1, vc3 = 2, vc4 = 3
    //How many rules we have (for not to exceed this number)
    static let definedNavigations = 4
    //This must return the identifier for this view on the Storyboard
    func storyboardIdentifier() -> String {
        //for this example's sake, we have a common prefix for every new view controller, if it's not the case, you can use a switch(self) here
        return "locationVC_\(self.rawValue + 1)"
    }
}

Now, let's build upon previous code:

3.- For clarity, let's change a little our previous LocationVC (that for this example, will have an Storyboard Identifier with the text "locationVC_1")

class LocationVC: CommonDataViewController {

    @IBOutlet weak var fromWhereLabel: UILabel!

    //This is optional, but improves clarity..here we take our AnyObject? variable data and transforms it into the type of data this view is excepting 
    var thisVCReceivedData: String? {
        return data as? String
    }

    override func viewWillAppear(animated: Bool) {
        if let textToShow = thisVCReceivedData {
            fromWhereLabel.text = textToShow
        }
    }
}

4.- Now, we trigger all of this in our didSelectRowAtIndexPath function.

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
    //Just to avoid tapping on a cell that doesn't have an UIViewController asociated
    if Navigation.definedNavigations > indexPath.row {
        //The view's instance on our Navigation enum to which we most go after tapping this cell
        let nextView = Navigation(rawValue: indexPath.row)!
        //The identifier of the destination CommonDataViewController's son in our Storyboard
        let identifier = nextView.storyboardIdentifier()
        //If everything exists...
        if let subjectCell = tableView.cellForRowAtIndexPath(indexPath), let destinationViewController = navigationController?.storyboard?.instantiateViewControllerWithIdentifier(identifier) as? CommonDataViewController {

            //here you can use a switch around "nextView" for passing different data to every View Controller..for this example, we just pass same String to everyone
            if let text = subjectCell.textLabel?.text {
                destinationViewController.data = text
            } else {
                destinationViewController.data = "Tapped Cell's textLabel is empty"
            }
            navigationController?.pushViewController(destinationViewController, animated: true)
        }
    }
}

Notice that you can achieve same results using protocols and delegate approach, this is just simpler to explain