Steven Steven - 2 months ago 22
iOS Question

UITableView add new rows to bottom with animation without reloading entire table

I have a UITableView and I want to be able to add new rows to the bottom with animation, I was using

tableView reloadData
and that was working alright, but I want animation. So I switched over too
tableView reloadSections
but the problem is if I have say 7 rows visible it animates all 7 of those rows. I only want it to animate adding the new row, and not every row on the screen as if it was being added.

Any idea of how to do this?


Under UITableView you can call a method

- (void) insertRowsAtIndexPaths:(NSArray*) indexPaths
              withRowAnimation: (UITableViewRowAnimation) animation

This allows you to specify the rows in section were you would like to insert a new cell and pass what animation you would like to use. Example code:

//  InsertingNewRowToBottomOfTableViewController.m
//  Testing-Code-Snippets

#import "InsertingNewRowToBottomOfTableViewController.h"

@interface InsertingNewRowToBottomOfTableViewController ()

#define kTestResuseCellIdentifier @"kTestResuseCell"

@implementation InsertingNewRowToBottomOfTableViewController
    NSMutableArray *testArray;

- (void) viewDidLoad
    [super viewDidLoad];
    self->testArray = [[NSMutableArray alloc] initWithArray:@[@"Test 1", @"Test 2", @"Test 3"]];

    UIRefreshControl *customRefreshControl = [[UIRefreshControl alloc] init];
    customRefreshControl.backgroundColor = [UIColor purpleColor];
    [customRefreshControl addTarget:self action:@selector(onRefresh:) forControlEvents:UIControlEventValueChanged];
    self.refreshControl = customRefreshControl;

- (void) didReceiveMemoryWarning
    [super didReceiveMemoryWarning];

- (void) onRefresh: (UIRefreshControl*) refreshControl
    [refreshControl endRefreshing];
    [self->testArray addObject:[NSString stringWithFormat:@"Test %lu", self->testArray.count + 1]];
    [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self->testArray.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];
    NSLog(@"Added a new cell to the bottom!");

#pragma mark - Table view data source

- (NSInteger) numberOfSectionsInTableView: (UITableView*) tableView
    return 1;

- (NSInteger) tableView: (UITableView*) tableView numberOfRowsInSection: (NSInteger) section
    return self->testArray.count;

- (UITableViewCell*) tableView: (UITableView*) tableView cellForRowAtIndexPath: (NSIndexPath*) indexPath
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kTestResuseCellIdentifier forIndexPath:indexPath];
    cell.textLabel.text = [self->testArray objectAtIndex:indexPath.row];
    return cell;

Update: Swift 3 Version

import UIKit

class InsertingNewRowToBottomOfTableViewController: UITableViewController
    private let testResuseCellIdentifier:String = "kTestResuseCell"
    private let testArray:NSMutableArray = NSMutableArray(array:["Test 1", "Test 2", "Test 3"])

    // MARK: - View Events

    override func viewDidLoad()

        let refreshControl:UIRefreshControl = UIRefreshControl()
        refreshControl.backgroundColor = UIColor.purple
        refreshControl.addTarget(self, action:#selector(self.onRefresh(refreshControl:)), for:.valueChanged)
        self.refreshControl = refreshControl

    override func didReceiveMemoryWarning()

    // MARK: - UIRefreshControl

    @objc private func onRefresh(refreshControl: UIRefreshControl)
        testArray.add("Test \(self.testArray.count + 1)")

        let indexPath:IndexPath = IndexPath(row:(self.testArray.count - 1), section:0)
        self.tableView.insertRows(at:[indexPath], with: .left)
        NSLog("Added a new cell to the bottom!")

    // MARK: - UITableViewDataSource

    override func numberOfSections(in tableView: UITableView) -> Int
        return 1

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
        return self.testArray.count

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
        let cell:UITableViewCell = tableView.dequeueReusableCell(withIdentifier:testResuseCellIdentifier)!
        cell.textLabel?.text = self.testArray.object(at: indexPath.row) as? String
        return cell