Is the following post still the accepted way of detecting when an instance of UITableView has scrolled to the bottom [in Swift], or has it been altered (as in: improved) since?
Problem detecting if UITableView has scrolled to the bottom
Thank you.
Is the following post still the accepted way of detecting when an instance of UITableView has scrolled to the bottom [in Swift], or has it been altered (as in: improved) since?
Problem detecting if UITableView has scrolled to the bottom
Thank you.
Try this:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let height = scrollView.frame.size.height
let contentYOffset = scrollView.contentOffset.y
let distanceFromBottom = scrollView.contentSize.height - contentYOffset
if distanceFromBottom < height {
print("You reached end of the table")
}
}
or you can try this way:
if tableView.contentOffset.y >= (tableView.contentSize.height - tableView.frame.size.height) {
/// you reached the end of the table
}
Swift 3
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.row + 1 == yourArray.count {
print("do something")
}
}
In Swift 4
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let isReachingEnd = scrollView.contentOffset.y >= 0
&& scrollView.contentOffset.y >= (scrollView.contentSize.height - scrollView.frame.size.height)
}
If you implement expandable UITableView/UICollectionView
, you may need to check scrollView.contentSize.height >= scrollView.frame.size.height
We can avoid using scrollViewDidScroll
and use tableView:willDisplayCell
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if indexPath.section == tableView.numberOfSections - 1 &&
indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
// Notify interested parties that end has been reached
}
}
This should work for any number of sections.
let maxContent =
table.contentSize.height - table.bounds.height + table.contentInset.bottom
and then ...
if table.contentOffset.y < -maxContent {
// the table is now "pulling" (bouncing) as the user
// moves their finger upwards more than is possible
}
https://stackoverflow.com/a/71350599/294884
For very dynamic tables contentSize can be tricky and realtime.
You can use the indexPathsForVisibleRows
or indexPathForRow(at: CGPoint)
, but I prefer indexPathsForRows(in: CGRect)
because it lets you pretty cleanly incorporate the content insets (in case the tableView is partially covered).
var data: [String] = ... // Your data displayed in tableView
...
guard !data.isEmpty else {
return
}
let lastIndexPath = IndexPath(row: data.count - 1, section: 0)
let topInset = tableView.contentInset.top
let bottomInset = tableView.contentInset.bottom
let visibleRect = tableView.bounds
.inset(by: .init(top: topInset, left: 0, bottom: bottomInset, right: 0))
let visibilePaths = tableView.indexPathsForRows(in: visibleRect) ?? []
if visiblePaths.contains(lastIndexPath) {
// isScrolledToBottom
}
The topInset
is not needed, I just added it for sake of completion so the visibleArea is actually the visible area.
If you have more than 1 section, you'll have to calculate the lastIndexPath
appropriately.