Implementing Scroll to Bottom Event iOS

Scroll to the End Event – iOS

Say you want to determine if somebody has scrolled to the bottom of your TableView. How would you go about doing that?

Basics

The complicated part of registering the event is actually determining when someday has scrolled to the end BUT not including events that include the bounce.

The second thing we need to know is that a UITableView is subclass of UIScrollView, so it has common delegate methods and common methods. The things that matter to us are

contentSize – the actual size of the content inside our tableView / scrollView. If you have 10 cells with a height of 100 each, that means you contentSize height will be 1,000.

contentOffset – the offset from the origin of the inner bounds of the content inside the scrollView. If you scroll to that 4th cell, that means you are contentOffset {0, 400}

contentInsets – the inset for a scrollView

First, a UITableView is actually in fact a UIScrollView, so whoever implements the delegate will also have to implement

- (void)scrollViewDidScroll:(UIScrollView *)scrollView;

This method will give you an event every time the user has scrolled position.

Simplest Way

Remember how I said the bouncing is the complicated part? Well if you can disable the bouncing on your tableView then you can implement a pretty straightforward solution

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat botomInset = scrollView.contentInset.bottom;
CGFloat bottomScrollY = offsetY + scrollHeight - bottomInset;
CGFloat boundary = scrollView.contentSize.height;
if (bottomScrollY == boundary) {
NSLog(@"Scrolled to the bottom");
}
}

Done! Pretty easy.

Harder Way

But honestly the bounce is a pretty useful part of iOS and gives a nice visual touch, so we can’t disable it all the time. So we have to think of a better way to handle this.

So we only want to have an event when the user has scrolled past the boundary line, but that means we have to keep state about wether the user has crossed that line or not. So let’s add a new variable.

@property (nonatomic, assign) BOOL scrolledToBottom;

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat botomInset = scrollView.contentInset.bottom;
CGFloat bottomScrollY = offsetY + scrollHeight - bottomInset;
CGFloat boundary = scrollView.contentSize.height;
if (bottomScrollY >= boundary && !self.scrolledToBottom) {
self.scrolledToBottom = YES;
NSLog(@"Scrolled to the bottom");
} else if (bottomScrollY < boundary ) {
self.scrolledToBottom = NO;
}
}

Huzzah! We now have a way to handle this, but we have a problem. We don’t always get the boundary line exactly. The ScrollView event doesn’t give us EVERY pixel that it passes by. Otherwise it would be to spammy.

So we need to have a bit of fuzziness.

- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat botomInset = scrollView.contentInset.bottom;
CGFloat bottomScrollY = offsetY + scrollHeight - bottomInset;
CGFloat fuzzFactor = 3;
CGFloat boundary = scrollView.contentSize.height-fuzzFactor;
if (bottomScrollY >= boundary && !self.scrolledToBottom) {
self.scrolledToBottom = YES;
NSLog(@"Scrolled to the bottom");
} else if (bottomScrollY < boundary ) {
self.scrolledToBottom = NO;
}
}

What does this mean? It means that if they cross any line within 3 pixels of the bottom, it will report that they have scrolled past the bottom of our tableView. Theoretically this doesn’t give you absolute certainty that it will trigger our event. But if you’re more and more paranoid, you can add fuzziness.

One More Thing

There’s one more edge case you should consider before implementing this and that’s if your content is smaller than your frame. The case when there’s no scrolling at all? I recommend adding an extra if condition

if (scrollView.contentSize.height > scrollView.height) {
CGFloat offsetY = scrollView.contentOffset.y;
CGFloat scrollHeight = scrollView.frame.size.height;
CGFloat botomInset = scrollView.contentInset.bottom;
CGFloat bottomScrollY = offsetY + scrollHeight - bottomInset;
CGFloat fuzzFactor = 3;
CGFloat boundary = scrollView.contentSize.height-fuzzFactor;
if (bottomScrollY >= boundary && !self.scrolledToBottom) {
self.scrolledToBottom = YES;
NSLog(@"Scrolled to the bottom");
} else if (bottomScrollY < boundary ) {
self.scrolledToBottom = NO;
}
}

Alternate Solutions

You can see alternate solutions to this problem but for a different reason (loading more cells) vs knowing an actual scrolled to the bottom event.

http://stackoverflow.com/questions/5137943/how-to-know-when-uitableview-did-scroll-to-bottom-in-iphone