Sweeper Sweeper - 1 year ago 117
Swift Question

Some table view cells become invisible after I added more cells. Why?

I am using a variation of the technique mentioned in this post to add and remove table view cells dynamically.

Initially, the table view cells looks like this:

enter image description here

Then, I add a new cell to section 1. Section 1 is the section above the "RESULTS" section. So I expect the new cell to appear below the cell with the name "h". But no! It turns into this!

enter image description here

The new cell is added in section 2 (The "RESULTS" section) and is added below the cell with the name "b". What's even more surprising is that the second cell in section 2 has disappeared!

Here is how I add the cell:

I have an array of cells here:

var cells: [[UITableViewCell]] = [[], [], []]

each subarray in the array represents a section. In
, I added some cells to sections 0 to 2 by calling:

addCellToSection(1, cell: someCell)

is defined as

func addCellToSection(section: Int, cell: UITableViewCell) {
tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: cells[section].endIndex - 1, inSection: section)], withRowAnimation: .Top)

And the table view data source methods are defined in the same way as the aforementioned post.

I tried to print the number of cells in each section when I add the cell:

print("no. of rows in section 1: \(self.tableView(tableView, numberOfRowsInSection: 1))")
print("no. of rows in section 2: \(self.tableView(tableView, numberOfRowsInSection: 2))")

And the printed values are consistent i.e. when I add a new cell, the no. of rows increase by 1. But the weird thing is that it keeps placing rows in the wrong position.

Extra info: how I create the cell:

I first dequeue the cells from the prototype cells. I then call
to get the text fields that are in the cell and add them to a
[(UITextField, UITextField)]
. Don't know whether this matters.

Answer Source

Okay so first of all, you should never store UITableView cells in some custom collection. This is and should be done by iOS, not you.

The data you are using to populate the cells are stored in some model I presume?

Your tableView should register cells using either: func registerClass(cellClass: AnyClass?, forCellReuseIdentifier identifier: String)


func registerNib(nib: UINib?, forCellReuseIdentifier identifier: String)

or using Prototype cells in the Xib/Storyboard.

I recommend this setup, or similar:

class MyModel {
        /* holds data displayed in cell */
        var name: String?
        var formula: String?
        init(name: String, formula: String) {
            self.name = name
            self.formula = formula

class MyCustomCell: UITableViewCell, UITextFieldDelegate {
    static var nibName = "MyCustomCell"

    @IBOutlet weak var nameTextField: UITextField!
    @IBOutlet weak var formulaTextField: UITextField!
    weak var model: MyModel?

    override func awakeFromNib() {
        nameTextField.delegate = self
        formulaTextField.delegate = self

    func updateWithModel(model: MyModel) {
        /* update labels, images etc in this cell with data from model */
        nameTextField.text = model.name
        formulaTextField.text = model.formula
        self.model = model

    /* This code only works if MyModel is a class, because classes uses reference type, and the value
     of the name and formula properies are changed in the model stored in the dictionary */
    func textFieldShouldEndEditing(textField: UITextField) -> Bool {
        let newText = textField.text
        switch textField {
        case nameTextField:
            model?.name = newText
        case formulaTextField:
            model?.formula = newText
            print("Needed by compiler..")

class MyController: UIViewController, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var tableVieW: UITableView!

    override func viewDidLoad() {
        /* This is not needed if you are using prototype cells in the Xib or Storyboard. 
         Convenient to use nib name as cell identifier */
        tableVieW.registerNib(UINib(nibName: MyCustomCell.nibName, bundle: nil), forCellReuseIdentifier: MyCustomCell.nibName)
        tableVieW.delegate = self
        tableVieW.dataSource = self

    private var dictionaryWithModelsForSection: Dictionary<Int, [MyModel]>!

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        let sectionCount = dictionaryWithModelsForSection.keys.count
        return sectionCount

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        guard let models: [MyModel] = modelsForSection(section) else {
            return 0
        let rowCount = models.count
        return rowCount

    private func modelsForSection(section: Int) -> [MyModel]? {
        guard section < dictionaryWithModelsForSection.count else {
            return nil
        let models = dictionaryWithModelsForSection[section]
        return models

    private func modelAtIndexPath(indexPath: NSIndexPath) -> MyModel? {
        guard let models = modelsForSection(indexPath.section) where models.count > indexPath.row else {
            return nil
        let model = models[indexPath.row]
        return model

    func addRowAtIndexPath(indexPath: NSIndexPath, withModel model: MyModel) {
        add(model: model, atIndexPath: indexPath)
        tableVieW.insertRowsAtIndexPaths([indexPath], withRowAnimation: .None)

    private func add(model model: MyModel, atIndexPath indexPath: NSIndexPath) {
        guard var models = modelsForSection(indexPath.section) where indexPath.row <= models.count else { return }
        models.insert(model, atIndex: indexPath.row)
        dictionaryWithModelsForSection[indexPath.section] = models

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(MyCustomCell.nibName, forIndexPath: indexPath)
        return cell

    func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
        guard let
            cell = cell as? MyCustomCell,
            model = modelAtIndexPath(indexPath) else { return }

If you want to insert a cell you can use the method addRowAtIndexPath:withModel i wrote in MyController above, you need to call that from some function creating the corresponding model...

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download