Add rows to UITableView when scrolled to bottom

31,249

Solution 1

Simply listen to the scrollViewDidScroll: delegate method, compare the content offset with the current possible offset and if lower then some threshold call your method to update the tableview. Don't forget to call [tableView reloadData] to make sure it reload the newly added data.

EDIT: Put together abstract code, not sure if it works, but should.

- (void)scrollViewDidScroll: (UIScrollView *)scroll {
     // UITableView only moves in one direction, y axis
     CGFloat currentOffset = scroll.contentOffset.y;
     CGFloat maximumOffset = scroll.contentSize.height - scroll.frame.size.height;

     // Change 10.0 to adjust the distance from bottom
     if (maximumOffset - currentOffset <= 10.0) {
          [self methodThatAddsDataAndReloadsTableView];
     }
}

Solution 2

Unless it´s a little late, but i think i found a better solution:

instead of

 - (void)scrollViewDidScroll: (UIScrollView)scroll

i used

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate

This is much more convenient, cause the event is only triggered once. I used this code in my application to load more rows in my tableview at the bottom (maybe you recognize this type of reloading from the facebook application - only difference that they are updating at the top).

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {    
    NSInteger currentOffset = scrollView.contentOffset.y;
    NSInteger maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;

    if (maximumOffset - currentOffset <= -40) {
        NSLog(@"reload");
    }
}

Hope anyone will help this.

Solution 3

I use this snippet. in tableView:willDisplayCell:forRowAtIndexPath: I check, if the cell with the last index path is about to be diplayed.

For a tableView with one section:

[indexPath isEqual:[NSIndexPath indexPathForRow:[self tableView:self.tableView numberOfRowsInSection:0]-1 inSection:0]

with more sections:

[indexPath isEqual:[NSIndexPath indexPathForRow:[self tableView:self.tableView numberOfRowsInSection:0]-1 inSection:[self numberOfSectionsInTableView:self.tableView]-1]

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    if(!_noMoreDataAvailable)
    {
        if ([indexPath isEqual:[NSIndexPath indexPathForRow:[self tableView:self.tableView numberOfRowsInSection:0]-1 inSection:0]])
        {
            [self.dataSourceController fetchNewData];
        }
    }
}

after fetching the dataSourceController will inform the tableView delegate and this will reload the data.

Solution 4

Swift

Here is the Swift version of @user944351's answer:

func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) {
    
    let currentOffset = scrollView.contentOffset.y
    let maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height
    
    if maximumOffset - currentOffset <= -40 {
        print("reload")
    }
}

Just add this method to your UITableView delegate class. I found -40 too large but you can adjust it according to your needs.

More info

  • See my fuller answer that has an example of method to reload data using limits and offsets for the database.

Solution 5

NSInteger currentOffset = scrollView.contentOffset.y;
NSInteger maximumOffset = scrollView.contentSize.height - scrollView.frame.size.height;

// Hit Bottom?
if ((currentOffset > 0) && (maximumOffset - currentOffset) <= 10) {
   // Hit Bottom !! 
}
Share:
31,249
aherlambang
Author by

aherlambang

iOS and web developer passionate on building apps

Updated on June 11, 2020

Comments

  • aherlambang
    aherlambang about 4 years

    Is there a way to trigger an event, such as with an IBAction, when a user scrolls to the bottom of a UITableView? I would like to add more rows if this happens. How do I do this?

  • aherlambang
    aherlambang about 13 years
    because I would like to call a REST web service to add more rows as there are more entries that can be seen by the user, instead of having a "load more posts" kind of thing, why not do it automatically
  • Henri Normak
    Henri Normak about 13 years
    Don't have a project atm to quickly show something in. I presume your viewController is the delegate of the tableView. If so, just add scrollViewDidScroll: to the viewController and use that as the place to trigger the update method. Comparing the contentOffset.y with maximum (contenSize.y - tableView.frame.size.height) should be quite straightforward.
  • Henri Normak
    Henri Normak about 13 years
    The key is to realize that UITableView is a subclass of UIScrollView and it passes on the delegate methods as well.
  • aherlambang
    aherlambang about 13 years
    and will this be triggered once the scrolling is stopped or will it also execute while scrolling?
  • Henri Normak
    Henri Normak about 13 years
    I believe this is triggered every single time the contentOffset changes, meaning if you scroll, this will get triggered a lot. Which reminds me, if you are doing a request from the web, you should probably disable this system for the time being when the request is ongoing and reenable once content has been added. Just add a boolean somewhere isUpdating and check that along with the offset difference.
  • aherlambang
    aherlambang about 13 years
    why is it also firing the method [self methodThatAddsDataAndReloadsTableView];
  • Henri Normak
    Henri Normak about 13 years
    What do you mean by that? I added that line just so you know where to put all your updating stuff into, you started the question with a desire to trigger an event when the scrollView was at the bottom, that event should go instead of the [self methodThatAddsDataAndReloadsTableView]; line.
  • Ben
    Ben over 10 years
    This is what I needed to only make a single call to my server. - (void)scrollViewDidScroll: is triggered to many times to make a call to your server. Unless you do as @Henri Normak suggested above in his comments and add a check for updating. This is much simpler. Thanks @user944351
  • Rob
    Rob over 10 years
    Brilliant answer, much prefer this compared scrollViewDidScroll option - thanks vikingosegundo!
  • Sjoerd Perfors
    Sjoerd Perfors about 10 years
    If you use loading or error cells, be sure to have a small check like: if(indexPath.row > 5) or simple set the boolean noMoreDataAvailble to false.
  • User
    User almost 10 years
    I tested this but it only works when the drag starts when the table view is already near to the bottom. If it's a long scroll, the if condition is not met even if it ends exactly at the bottom.
  • User
    User almost 10 years
    Makes sense since the method is called "end dragging" so I think is called with the offset when the user ends the drag gesture. But the list can continue scrolling. And it should load automatically when it reaches the bottom, so it looks like scrollViewDidScroll is correct.
  • Robert J. Clegg
    Robert J. Clegg about 9 years
    Not great if you want to load the data a little way before the user gets to the last cell. I find it 'flows' better when you load it a few cells before
  • vikingosegundo
    vikingosegundo about 9 years
    @Tander, you should be able to change that to an earlier indexPath.
  • iOS Lifee
    iOS Lifee over 6 years
    Same code in swift 3 let currentOffset: Int = Int(scrollView.contentOffset.y) let maximumOffset = Int(scrollView.contentSize.height - scrollView.frame.size.height) if maximumOffset - currentOffset <= -40 { print("got") }
  • daniel
    daniel over 5 years
    Is this a callback function that is called by the system?