A.Jam A.Jam - 2 months ago 34
Swift Question

Why is my tableView variable returning nil?

I am using a

UISearchController
in my project. I initiate the search controller by supplying the
init(searchResultsController)
method with an
UIViewController
object that manages a tableView. The code for this object is:

import UIKit

class ResultsTableViewController: UIViewController {

@IBOutlet weak var tableView: UITableView!
var list: [String] = []

override func viewDidLoad() {
super.viewDidLoad()
}
}

extension ResultsTableViewController: UITableViewDelegate{}

extension ResultsTableViewController: UITableViewDataSource{
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return list.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Cell")

if cell == nil {
cell = UITableViewCell(style: .Default, reuseIdentifier: "Cell")
cell?.textLabel?.text = list[indexPath.row]
} else {
cell?.textLabel!.text = list[indexPath.row]
}
return cell!
}
}


Furthermore, when I try to access the
resultsTableViewController.tableView
from the
updateSearchResultsForSearchController(searchController: UISearchController)
method of the
UISearchResultsUpdating
protocol to populate its "list" array, it gives me an error. The tableView returns nil and the app crashes. I would like to point out that I have connected the data source, delegate, and the
IBOutlet
variable of the tableView to the appropriate view controller. I was hoping for someone to explain to me why this happens? I think I have a misunderstanding in the life cycle of the
ResultsTableViewController
. Lastly, when I drag a TableViewController from the storyboard instead of making my own table view controller from scratch everything works smoothly without any errors! Can you please help me understand whats going on here?

Edit: The code for my initial view controller is:

import UIKit

class FirstViewController: UIViewController {

@IBOutlet weak var tableView: UITableView!
var resultsTableViewController = ResultsTableViewController()
var searchController: UISearchController!
let list = ["Apple", "Orange", "Bananas","Kiwi", "Cucumbers", "Apricot","Peach", "Cherry", "Mangosteen","Strawberry", "Blueberry", "Raspberry","Watermelon", "Persimmon", "plums","Papaya", "Jackfruit", "Lichi"]
var filteredList: [String]!


override func viewDidLoad() {
super.viewDidLoad()
setUpSearchController()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

func setUpSearchController(){
searchController = UISearchController(searchResultsController: resultsTableViewController)
searchController.dimsBackgroundDuringPresentation = false
searchController.searchResultsUpdater = self
searchController.hidesNavigationBarDuringPresentation = true
tableView.tableHeaderView = searchController.searchBar
tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
}

}

extension FirstViewController: UITableViewDelegate, UITableViewDataSource{

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

return list.count
}

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

var cell = tableView.dequeueReusableCellWithIdentifier("cell")
if cell == nil {
cell = UITableViewCell(style: .Default, reuseIdentifier: "cell")
cell?.textLabel?.text = list[indexPath.row]
}else {
cell?.textLabel?.text = list[indexPath.row]
}
return cell!
}

}

extension FirstViewController: UISearchResultsUpdating{

func updateSearchResultsForSearchController(searchController: UISearchController) {
filteredList = list.filter({
item in
return item.containsString(searchController.searchBar.text!)
})
resultsTableViewController.list = filteredList
resultsTableViewController.tableView.reloadData()
}
}

Answer

var resultsTableViewController = ResultsTableViewController()

creates a new ResultsTableViewController but it isn't linked to your storyboard scene, so none of the @IBOutlets will be set. You need to set an identifier for your scene (say resultsViewController) and then use that to instantiate the view controller from the storyboard:

var resultsTableViewController: ResultsTableViewController!

override func viewDidLoad() {
    super.viewDidLoad()
    setUpSearchController()
}

func setUpSearchController(){

    let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
    self.resultsTableViewController = storyboard.instantiateViewControllerWithIdentifer("resultsViewController") as! ResultsTableViewController

    searchController = UISearchController(searchResultsController: resultsTableViewController)
    searchController.dimsBackgroundDuringPresentation = false
    searchController.searchResultsUpdater = self
    searchController.hidesNavigationBarDuringPresentation = true
    tableView.tableHeaderView = searchController.searchBar
    tableView.scrollToRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 0), atScrollPosition: UITableViewScrollPosition.Top, animated: false)
}