iOS Swift - Filtering contents of UITableView that has custom cell with Search Bar and Search Display

Andrea Venanzi picture Andrea Venanzi · Nov 28, 2014 · Viewed 8.5k times · Source

I have an issue with Search Bar and Search Display in a Swift project for iPhone. I added one UITableView with custom cells to a View. Cell have Indetifier set to "QuestionCell" and Class set to "QuestionCellTableViewCell". I added to that cell two label and I added outlets for the labels to cell class. My goal is to filter the contents of the UITableView.

This is my ViewController: DomandeViewController.swift

import UIKit

class DomandeViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchDisplayDelegate, UISearchBarDelegate {

    var arrayDomande = [Domanda]()
    var areaDomande: Area = Area()
    var argomentoDomande: Argomento = Argomento()
    var domandeFiltrate = [Domanda]()

    @IBOutlet weak var questionTableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        arrayDomande = ModelManager.instance.getQuestionsWithArguments(inAreaId: areaDomande.id, aboutId: argomentoDomande.id)
        self.searchDisplayController!.searchResultsTableView.registerClass(QuestionCellTableViewCell.classForCoder(), forCellReuseIdentifier: "QuestionCell")
    }

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        if(tableView == self.searchDisplayController!.searchResultsTableView){
            return 1
        }else{
            return 1
        }
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if tableView == self.searchDisplayController!.searchResultsTableView {
            return domandeFiltrate.count
        } else {
            return arrayDomande.count
        }
    }

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

        var cell = tableView.dequeueReusableCellWithIdentifier("QuestionCell") as QuestionCellTableViewCell

        var domanda = Domanda()

        if tableView == searchDisplayController!.searchResultsTableView {
            domanda = domandeFiltrate[indexPath.row]
        } else {
            domanda = arrayDomande[indexPath.row]
        }

        cell.testoLabel.text = domanda.testo
        cell.numeroLabel.text = String(domanda.numero)

        return cell
    }

    func filterContentForSearchText(searchText: String) {
        // Filter the array using the filter method
        filteredDomande = arrayDomande.filter({( domanda: Domanda) -> Bool in
            let stringMatch = domanda.testo.rangeOfString(searchText)
            return stringMatch != nil
        })
    }

    func searchDisplayController(controller: UISearchDisplayController, shouldReloadTableForSearchString searchString: String!) -> Bool {
        self.filterContentForSearchText(searchString)
        return true
    }
}

This is my QuestionCellTableViewCell.swift

import UIKit

class QuestionCellTableViewCell: UITableViewCell {

    @IBOutlet weak var testoLabel: UILabel!
    @IBOutlet weak var numeroLabel: UILabel!


    override func awakeFromNib() {
        super.awakeFromNib()
        // Initialization code
    }

    override func setSelected(selected: Bool, animated: Bool) {
        super.setSelected(selected, animated: animated)

        // Configure the view for the selected state
    }

}

And this is my Domanda.swift

import UIKit

class Domanda: NSObject {
    var numero: Int = Int()
    var testo: String = String()
    var preferita: Bool = Bool()
    var visualizzato: Int = Int()
    var area: Int = Int()
    var argomento: Int = Int()
}

Everything works fine, until I search a string in Search Bar: the application crashes and the console returns:

"fatal error: unexpectedly found nil while unwrapping an Optional value"

With debug I see that "cell" exists, but its outlets (testoLabel and numeroLabel) are nil and so the app crashes on

cell.testoLabel.text = domanda.testo

If I remove in viewDidLoad()

self.searchDisplayController!.searchResultsTableView.registerClass(QuestionCellTableViewCell.classForCoder(), forCellReuseIdentifier: "QuestionCell")

the app crashes when is entered some text in Search Bar with same error when executes

var cell = tableView.dequeueReusableCellWithIdentifier("QuestionCell") as QuestionCellTableViewCell

If I add in viewDidLoad()

self.questionTableView.registerClass(QuestionCellTableViewCell.classForCoder(), forCellReuseIdentifier: "QuestionCell")

the app keep crashing at cell.testoLabel.text but this time at startup.

Some advice for me? Thank you very much, I appreciate any help!

Andrea

EDIT:

Today I did other tests and here's the result: if I use the cells of searchResultsTableView as Basic cells everything works fine, but if I try to use the outlets of my custom cell the application crashes. This is the working code:

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

        var cell = tableView.dequeueReusableCellWithIdentifier("QuestionCell") as QuestionCellTableViewCell
        var domanda = Domanda()

        if tableView == self.searchDisplayController!.searchResultsTableView {
            domanda = filteredDomande[indexPath.row]

            cell.textLabel.text = domanda.testo
        } else {
            domanda = arrayDomande[indexPath.row]

            cell.testoLabel.text = domanda.testo
            cell.numeroLabel.text = String(domanda.numero)
        }

        return cell
    }

In the end I can't seem to use my custom cell with searchResultsTableView. Where is my error? Thank you so much again!

Answer

Andrea Venanzi picture Andrea Venanzi · Nov 30, 2014

Finally I found a solution. I added a new user interface empty file to my project, then I created a cell in this new nib file and I assigned to it my custom cell class. In Interface Builder I set Prototype Cells to 0 for questionTableView and then I registered Nib forCellReuseIdentifier in viewDidLoad() both for questionTableView that for searchResultsTableView. This is my code:

var cellIdentifier = "questionCell"

@IBOutlet weak var questionTableView: UITableView!

override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        arrayDomande = ModelManager.instance.getQuestionsWithArguments(inAreaId: areaDomande.id, aboutId: argomentoDomande.id)

        var nib = UINib(nibName: "QuestionCellTableViewCell", bundle: nil)
        self.searchDisplayController!.searchResultsTableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
        self.questionTableView.registerNib(nib, forCellReuseIdentifier: cellIdentifier)
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        var cell: QuestionCellTableViewCell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as QuestionCellTableViewCell
        var domanda = Domanda()

        if tableView == self.searchDisplayController!.searchResultsTableView {
            domanda = filteredDomande[indexPath.row]
        } else {
            domanda = arrayDomande[indexPath.row]
        }
        cell.testoLabel.text = domanda.testo
        cell.numeroLabel.text = String(domanda.numero)

        return cell
    }