I would like to know when a UITableView
did scroll to bottom in order to load and show more content, something like a delegate or something else to let the controller know when the table did scroll to bottom.
How can I do this?
Overview. Table views in iOS display rows of vertically scrolling content in a single column. Each row in the table contains one piece of your app's content. For example, the Contacts app displays the name of each contact in a separate row, and the main page of the Settings app displays the available groups of settings ...
Swift version: 5.6. Index paths describe an item's position inside a table view or collection view, storing both its section and its position inside that section.
Since a scrollView has a panGesture we can check the velocity of that gesture. If the tableView was programmatically scrolled the velocity in both x and y directions is 0.0. By checking this velocity we can determine if the user scrolled the tableView because the panGesture has a velocity.
in the tableview delegate do something like this
ObjC:
- (void)scrollViewDidScroll:(UIScrollView *)aScrollView {
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
// NSLog(@"offset: %f", offset.y);
// NSLog(@"content.height: %f", size.height);
// NSLog(@"bounds.height: %f", bounds.size.height);
// NSLog(@"inset.top: %f", inset.top);
// NSLog(@"inset.bottom: %f", inset.bottom);
// NSLog(@"pos: %f of %f", y, h);
float reload_distance = 10;
if(y > h + reload_distance) {
NSLog(@"load more rows");
}
}
Swift:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset
let bounds = scrollView.bounds
let size = scrollView.contentSize
let inset = scrollView.contentInset
let y = offset.y + bounds.size.height - inset.bottom
let h = size.height
let reload_distance:CGFloat = 10.0
if y > (h + reload_distance) {
print("load more rows")
}
}
Modified neoneyes answer a bit.
This answer targets those of you who only wants the event to be triggered once per release of the finger.
Suitable when loading more content from some content provider (web service, core data etc). Note that this approach does not respect the response time from your web service.
- (void)scrollViewDidEndDragging:(UIScrollView *)aScrollView
willDecelerate:(BOOL)decelerate
{
CGPoint offset = aScrollView.contentOffset;
CGRect bounds = aScrollView.bounds;
CGSize size = aScrollView.contentSize;
UIEdgeInsets inset = aScrollView.contentInset;
float y = offset.y + bounds.size.height - inset.bottom;
float h = size.height;
float reload_distance = 50;
if(y > h + reload_distance) {
NSLog(@"load more rows");
}
}
add this method in the UITableViewDelegate
:
-(void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGFloat height = scrollView.frame.size.height;
CGFloat contentYoffset = scrollView.contentOffset.y;
CGFloat distanceFromBottom = scrollView.contentSize.height - contentYoffset;
if(distanceFromBottom < height)
{
NSLog(@"end of the table");
}
}
None of the answers above helped me, so I came up with this:
- (void)scrollViewDidEndDecelerating:(UIScrollView *)aScrollView
{
NSArray *visibleRows = [self.tableView visibleCells];
UITableViewCell *lastVisibleCell = [visibleRows lastObject];
NSIndexPath *path = [self.tableView indexPathForCell:lastVisibleCell];
if(path.section == lastSection && path.row == lastRow)
{
// Do something here
}
}
in Swift
you can do something like this. Following condition will be true every time you reach end of the tableview
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row+1 == postArray.count {
println("came to last row")
}
}
The best way is to test a point at the bottom of the screen and use this method call when ever the user scrolls (scrollViewDidScroll
):
- (NSIndexPath *)indexPathForRowAtPoint:(CGPoint)point
Test a point near the bottom of the screen, and then using the indexPath it returns check if that indexPath is the last row then if it is, add rows.
Use – tableView:willDisplayCell:forRowAtIndexPath:
(UITableViewDelegate
method)
Simply compare the indexPath
with the items in your data array (or whatever data source you use for your table view) to figure out if the last element is being displayed.
Docs: http://developer.apple.com/library/ios/documentation/uikit/reference/UITableViewDelegate_Protocol/Reference/Reference.html#//apple_ref/occ/intfm/UITableViewDelegate/tableView:willDisplayCell:forRowAtIndexPath:
UITableView
is a subclass of UIScrollView
, and UITableViewDelegate
conforms to UIScrollViewDelegate
. So the delegate you attach to the table view will get events such as scrollViewDidScroll:
, and you can call methods such as contentOffset
on the table view to find the scroll position.
NSLog(@"%f / %f",tableView.contentOffset.y, tableView.contentSize.height - tableView.frame.size.height);
if (tableView.contentOffset.y == tableView.contentSize.height - tableView.frame.size.height)
[self doSomething];
Nice and simple
Building on @Jay Mayu's answer, which I felt was one of the better solutions:
Objective-C
// UITableViewDelegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
// Need to call the service & update the array
if(indexPath.row + 1 == self.sourceArray.count) {
DLog(@"Displayed the last row!");
}
}
Swift 2.x
// UITableViewDelegate
func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) {
if (indexPath.row + 1) == sourceArray.count {
print("Displayed the last row!")
}
}
Here is the swift 3.0 version code.
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let offset = scrollView.contentOffset
let bounds = scrollView.bounds
let size = scrollView.contentSize
let inset = scrollView.contentInset
let y: Float = Float(offset.y) + Float(bounds.size.height) + Float(inset.bottom)
let height: Float = Float(size.height)
let distance: Float = 10
if y > height + distance {
// load more data
}
}
I generally use this to load more data , when last cell starts display
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row == myDataArray.count-1) {
NSLog(@"load more");
}
}
Taking neoneye excellent answers but in swift, and renaming of the variables..
Basically we know we have reached the bottom of the table view if the yOffsetAtBottom
is beyond the table content height.
func isTableViewScrolledToBottom() -> Bool {
let tableHeight = tableView.bounds.size.height
let contentHeight = tableView.contentSize.height
let insetHeight = tableView.contentInset.bottom
let yOffset = tableView.contentOffset.y
let yOffsetAtBottom = yOffset + tableHeight - insetHeight
return yOffsetAtBottom > contentHeight
}
My solution is to add cells before tableview will decelerate on estimated offset. It's predictable on by velocity.
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)offset {
NSLog(@"offset: %f", offset->y+scrollView.frame.size.height);
NSLog(@"Scroll view content size: %f", scrollView.contentSize.height);
if (offset->y+scrollView.frame.size.height > scrollView.contentSize.height - 300) {
NSLog(@"Load new rows when reaches 300px before end of content");
[[DataManager shared] fetchRecordsNextPage];
}
}
Update for Swift 3
Neoneye's answer worked best for me in Objective C, this is the equivalent of the answer in Swift 3:
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
let offset: CGPoint = scrollView.contentOffset
let bounds: CGRect = scrollView.bounds
let size: CGSize = scrollView.contentSize
let inset: UIEdgeInsets = scrollView.contentInset
let y: CGFloat = offset.y + bounds.size.height - inset.bottom
let h: CGFloat = size.height
// print("offset: %f", offset.y)
// print("content.height: %f", size.height)
// print("bounds.height: %f", bounds.size.height)
// print("inset.top: %f", inset.top)
// print("inset.bottom: %f", inset.bottom)
// print("position: %f of %f", y, h)
let reloadDistance: CGFloat = 10
if (y > h + reloadDistance) {
print("load more rows")
}
}
I want perform some action on my any 1 full Tableviewcell.
So the code is link the :
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
NSArray* cells = self.tableView.visibleCells;
NSIndexPath *indexPath = nil ;
for (int aIntCount = 0; aIntCount < [cells count]; aIntCount++)
{
UITableViewCell *cell = [cells objectAtIndex:aIntCount];
CGRect cellRect = [self.tableView convertRect:cell.frame toView:self.tableView.superview];
if (CGRectContainsRect(self.tableView.frame, cellRect))
{
indexPath = [self.tableView indexPathForCell:cell];
// remain logic
}
}
}
May this is help to some one.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With