80

I'm working on a project where I have two UITableViews and two UITextFields, when the user presses the button the data in the first textField should go into the tableView and the second go into the second tableView. My problem is that I don't know how to put data in the tableView each time the user pushes the button, I know how to insert data with tableView:cellForRowAtIndexPath: but that works for one time as far as I know. So what method can I use to update the tableView each time the user hits the button?

Bhavin Bhadani
  • 22,224
  • 10
  • 78
  • 108
code Horizons
  • 841
  • 2
  • 8
  • 8
  • What have you tried? You say "tableView:cellForRowAtIndexPath:" only works "as far as" you know. Why not try it in your button's method? – Max von Hippel Aug 07 '15 at 07:11
  • 1
    @Max von Hippel what I did was : the tableView takes the data from an array so when I append an item to the array I use this method : tableView.reloadData this way when ever I append an item the array will go to the "CellForRowAtIndexPath" and pull the informations again from the array :) – code Horizons Aug 07 '15 at 15:34

5 Answers5

183

Use beginUpdates and endUpdates to insert a new cell when the button clicked.

As @vadian said in comment, begin/endUpdates has no effect for a single insert/delete/move operation

First of all, append data in your tableview array

Yourarray.append([labeltext])  

Then update your table and insert a new row

// Update Table Data
tblname.beginUpdates()
tblname.insertRowsAtIndexPaths([
NSIndexPath(forRow: Yourarray.count-1, inSection: 0)], withRowAnimation: .Automatic)
tblname.endUpdates()

This inserts cell and doesn't need to reload the whole table but if you get any problem with this, you can also use tableview.reloadData()


Swift 3.0

tableView.beginUpdates()
tableView.insertRows(at: [IndexPath(row: yourArray.count-1, section: 0)], with: .automatic)
tableView.endUpdates()

Objective-C

[self.tblname beginUpdates];
NSArray *arr = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:Yourarray.count-1 inSection:0]];
[self.tblname insertRowsAtIndexPaths:arr withRowAnimation:UITableViewRowAnimationAutomatic];
[self.tblname endUpdates];
Bhavin Bhadani
  • 22,224
  • 10
  • 78
  • 108
  • 3
    Nice that you have provided the Objective-C version as well. It helps sometimes!! – Rashmi Ranjan mallick Oct 09 '15 at 04:54
  • 1
    @RashmiRanjanmallick ...yes same thing I thought and edit my answer...so people understand translations :) – Bhavin Bhadani Oct 09 '15 at 04:56
  • 7
    `begin/endUpdates` has no effect for a single insert/delete/move operation. – vadian Apr 28 '16 at 13:37
  • No, you can omit `begin/endUpdates` for a single insert operation and it is working anyway. – vadian May 09 '16 at 12:13
  • 1
    @vadian hmm ... thats what I think ... but what do you mean by `begin/endUpdates has no effect for a single insert/delete/move operation` this sentence ??? sorry for too many questions .. – Bhavin Bhadani May 09 '16 at 12:16
  • 3
    *No effect* means there is no difference if the lines are there or not. You need the lines only for e.g. an `insert` line followed by a `delete` line or multiple calls of the same line in a repeat loop. – vadian May 09 '16 at 12:19
  • Does the the call to `beginUpdates`/`endUpdates` have to be on the main thread? or it will handle that itself automatically? – mfaani Aug 21 '17 at 10:33
  • Looks like `begin/endUpdates` would cause the `tableView` to calculate the size of every cell. The scrolling get smoother after I remove them. – Kimi Chiu Jan 16 '18 at 08:31
  • this works fine for a tableView with one section , but with multiple dynamic sections it crashes if the item is being placed into a new section and also for the first item added to the tableview. So for multiple sections check your dataSource to see if it's the first item in the section. if so use insertSection instead of insertRow, otherwise use the above. – alionthego Feb 13 '18 at 08:04
  • doesn't inserting at `row: yourArray.count-1` insert the new row at the second-to-last row? – matt b Jun 06 '18 at 01:21
  • 1
    @mattb not really .. array count starts from 1 like we have 10 rows and index starts from 0. So to insert the 11th row, right now we have index till 9. So we have to set -1 means at 10th index we need to insert 11th row. hope u understand now :) – Bhavin Bhadani Jun 06 '18 at 05:02
33

Swift 5.0, 4.0, 3.0 Updated Solution

Insert at Bottom

self.yourArray.append(msg)

self.tblView.beginUpdates()
self.tblView.insertRows(at: [IndexPath.init(row: self.yourArray.count-1, section: 0)], with: .automatic)
self.tblView.endUpdates()

Insert at Top of TableView

self.yourArray.insert(msg, at: 0)
self.tblView.beginUpdates()
self.tblView.insertRows(at: [IndexPath.init(row: 0, section: 0)], with: .automatic)
self.tblView.endUpdates()
Sourabh Sharma
  • 8,222
  • 5
  • 68
  • 78
  • How's this achieved with a collection view? I can insert multiple items with ease.... but this is inserting at index 0. When the item is selected the previous index 0 is shown. – scrainie Jan 22 '17 at 03:12
22

Here is your code for add data into both tableView:

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

    @IBOutlet weak var table1Text: UITextField!
    @IBOutlet weak var table2Text: UITextField!
    @IBOutlet weak var table1: UITableView!
    @IBOutlet weak var table2: UITableView!

    var table1Data = ["a"]
    var table2Data = ["1"]

    override func viewDidLoad() {
        super.viewDidLoad()

    }

    @IBAction func addData(sender: AnyObject) {

        //add your data into tables array from textField
        table1Data.append(table1Text.text)
        table2Data.append(table2Text.text)

        dispatch_async(dispatch_get_main_queue(), { () -> Void in
            //reload your tableView
            self.table1.reloadData()
            self.table2.reloadData()
        })


        table1Text.resignFirstResponder()
        table2Text.resignFirstResponder()
    }

    //delegate methods
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if tableView == table1 {
            return table1Data.count
        }else if tableView == table2 {
            return table2Data.count
        }
        return Int()
    }

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

        if tableView == table1 {
            let cell = table1.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell

            let row = indexPath.row
            cell.textLabel?.text = table1Data[row]

            return cell
        }else if tableView == table2 {

            let cell = table2.dequeueReusableCellWithIdentifier("Cell1", forIndexPath: indexPath) as! UITableViewCell

            let row = indexPath.row
            cell.textLabel?.text = table2Data[row]

            return cell
        }

        return UITableViewCell()
    }
}

And your result will be:

enter image description here

Dharmesh Kheni
  • 71,228
  • 33
  • 160
  • 165
3

For Swift 5

Remove Cell

    let indexPath = [NSIndexPath(row: yourArray-1, section: 0)]
    yourArray.remove(at: buttonTag)
    self.tableView.beginUpdates()

    self.tableView.deleteRows(at: indexPath as [IndexPath] , with: .fade)
    self.tableView.endUpdates()
    self.tableView.reloadData()// Not mendatory, But In my case its requires

Add new cell

    yourArray.append(4)

    tableView.beginUpdates()
    tableView.insertRows(at: [
        (NSIndexPath(row: yourArray.count-1, section: 0) as IndexPath)], with: .automatic)
    tableView.endUpdates()
Shourob Datta
  • 1,886
  • 22
  • 30
  • 2
    Why do you create an NSIndexPath then cast it into IndexPath? Just create an IndexPath initially. I usually do it even simpler: `[0, 1]`. Zero is the section, 1 is the row. – Starsky Oct 08 '20 at 14:25
  • 1
    Yes, you can use IndexPath instead of NSIndexPath. – Shourob Datta Feb 16 '22 at 21:15
1

You can find this comment in Apple's UITableView framework:

// Use -performBatchUpdates:completion: instead of these methods, which will be deprecated in a future release.

So, you should use this:

tableView.performBatchUpdates { [unowned self] in
    tableView.insertRows(at: indexPaths, with: animation)
}
Victor Morei
  • 180
  • 4