Infinite scrolling for scrolling up a tableView? - ios

I have a chat messaging app where it loads my messages from earliest to latest, then auto scrolls to the bottom so the user sees the latest.
I'm only loading 25 messages, then when the user scrolls to the top, I'd like to upload the next 25 messages. It's proving to be really tricky and ending up in an infinite loop.
Here's what I'm doing:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
guard indexPath.row == 1 else { return }
fetchNextMessages(with: lastReference: lastMessage.reference)
}
Before the table finished scrolling to the bottom, it started fetching the next set which is wrong. So I tried this:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard scrollView.contentOffset.y <= 0 else { return }
fetchNextMessages(with: lastReference: lastMessage.reference)
}
However, same issue. Any better way to handle this?

You should note that this method is being called multiple times , you should make fetchingNow = false after you reload the table with new data , besides when you load the new content don't make the scroll to the oldest ones and leave the user to scroll up to see remaining
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard scrollView.contentOffset.y <= 0 else { return }
if(!fetchingNow)
{
fetchNextMessages(with: lastReference: lastMessage.reference)
}
}

Related

UITableView with Full Screen cells - pagination breaks after fetch more rows

My app uses a UITableView to implement a TikTok-style UX. Each cell is the height of the entire screen. Pagination is enabled, and this works fine for the first batch of 10 records I load. Each UITableViewCell is one "page". The user can "flip" through the pages by swiping, and initially each "page" fully flips as expected. However when I add additional rows by checking to see if the currently visible cell is the last one and then loading 10 more rows, the pagination goes haywire. Swiping results in a partially "flipped" cell -- parts of two cells are visible at the same time. I've tried various things but I'm not even sure what the problem is. The tableView seems to lose track of geometry.
Note: After the pagination goes haywire I can flip all the way back to the first cell. At that point the UITableView seems to regain composure and once again I'm able to flip correctly through all of the loaded rows, including the new ones.
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// Pause the video if the cell is ended displaying
if let cell = cell as? HomeTableViewCell {
cell.pause()
}
if let indices = tableView.indexPathsForVisibleRows {
for index in indices {
if index.row >= self.data.count - 1 {
self.viewModel!.getPosts()
break
}
}
}
}
In order to create a "Tik Tok" style UX, I ended up using the Texture framework together with a cloud video provider (mux.com). Works fine now.
I was facing the same issue and as I couldn't find a solution anywhere else here's how I solved it without using Texture:
I used the UITableViewDataSourcePrefetching protocol to fetch the new data to be inserted
extension TikTokTableView: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
viewModel.prefetchRows(at: indexPaths)
}
}
prefetchRows will execute the request if the visible cell is the last one, as in my case
func prefetchRows(at indexPaths: [IndexPath]) {
if indexPaths.contains(where: isLastCell) {
getPosts(type: typeOfPosts, offset: posts.count, lastPostId: lastPost)
}
}
private func isLastCell(for indexPath: IndexPath) -> Bool {
return indexPath.row == posts.count - 1
}
I have a weak var view delegate type TikTokTableViewDelegate in my view model to have access to a function insertItems implemented by my TikTokTableView. This function is used to inform the UITableView where to insert the incoming posts at
self.posts.append(contentsOf: response.posts)
let indexPathsToReload = self.calculateIndexPathToReload(from: response.posts)
self.view?.insertItems(at: indexPathsToReload)
private func calculateIndexPathToReload(from newPosts: [Post]) -> [IndexPath] {
let startIndex = posts.count - newPosts.count
let endIndex = startIndex + newPosts.count
print(startIndex, endIndex)
return (startIndex..<endIndex).map { IndexPath(row: $0, section: 0) }
}
and this is the insertItems function implemented in TikTokTableView and here is the key: If we try to insert those rows, the pagination of the table will fail and leave that weird offset, we have to store the indexPaths in a local property and insert them once the scroll animation has finished.
extension TikTokTableView: TikTokTableViewDelegate {
func insertItems(at indexPathsToReload: [IndexPath]) {
DispatchQueue.main.async {
// if we try to insert rows in the table, the scroll animation will be stopped and the cell will have a weird offset
// that's why we keep the indexPaths and insert them on scrollViewDidEndDecelerating(:)
self.indexPathsToReload = indexPathsToReload
}
}
}
Since UITableView is a subclass of UIScrollView, we have access to scrollViewDidEndDecelerating, this func is triggered at the end of a user's scroll and this is the time when we insert the new rows
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if !indexPathsToReload.isEmpty {
tableView.insertRows(at: indexPathsToReload, with: .none)
indexPathsToReload = []
}
}

UITableView Pagination willDisplay call again and again swift

I am trying to implement pagination in UITableView after searching this question on stackoverflow and google i got many method but facing same problem in all the solution, in willDisplay last cell method calling again and again so pagination does not work it load data in loop before i move to last cell please guide me self.isGettingMoreData is false when all data is fetched.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if !self.isGettingMoreData && indexPath.row >= (self.datasource.count - 1) {
self.isGettingMoreData = true
loadmore(pagenum)
}
}
and i am using scrolview like this but in this way i need to drag at the end to load more i don't want to like this way
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
//Bottom Refresh
if scrollView == table{
if ((scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height)
{
if !isLoading{
isLoading = true
loadmore()
To implement pagination in a table view or a collection view you can put your code in scrollViewDidScroll method. Here is a sample code that I use:
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if moreResultsAvailable {
if let lastVisibleIndexPath = self.collectionView.indexPathsForVisibleItems.last {
if let dataCount = dataArray.count {
let dif = abs(dataCount - lastVisibleIndexPath.row)
if dif == 0 {
pageNo += 1
fetchMoreData()
}
}
}
}
}

Scroll on Demand keeps calling

I am trying to implement scroll on demand call. Until I scroll at the bottom of the screen, it never calls doPaging() method which is good and then downloads another batch of dataset( 20 more items). However when it reaches at the bottom of screen first time and keeps calling even small scroll to the bottom.
I wonder what I am missing in the following implementation.
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath)
{
if indexPath.row == self.products.count - 1 && !isWating {
isWating = true
self.pageNumber += 1
self.doPaging()
}
}
func doPaging()
{
fetchProducts(page: pageNumber , completion: { success in
if let products = success as? Products
{
// keeps calling
DispatchQueue.main.async {
self.products.append(contentsOf:products)
self.isWating = false;
self.productTableView.reloadData()
}
}
})
}
Use this method. This will work surely
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
//Bottom Refresh
if ((scrollView.contentOffset.y + scrollView.frame.size.height) >= scrollView.contentSize.height) {
if !isWating {
isWating = true
self.doPaging()
}
}
}

Load Earlier With smooth scroll

I am working on chat application. In my app load earlier message feature implemented which is not smooth and accurate like whatsapp.
I am using UITableview for chat listing and fetching more data using
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if scrollView.contentOffset.y >= 0 && scrollView.contentOffset.y <= 50{
print(" scrollViewDidScroll Load Earlier start- \(Utils.stringFromNSDate(Date(), inMillisec: true, useUTC: true)!)")
if !self.messageFetcher.isHideLoadMoreDisplay{
if self.messageFetcher.arrayOfallChatMessagesData.count > 0 && !isCheckLoading{
self.isCheckLoading = true
let message = self.messageFetcher.arrayOfallChatMessagesData[0]
self.messageIdForMessageDisplay = (message.chatMessageId )
self.loadMoreDataLoad(threadId: self.messageFetcher.chatThreadId, isloadFromServer: false)
}
}
print(" scrollViewDidScroll Load Earlier end- \(Utils.stringFromNSDate(Date(), inMillisec: true, useUTC: true)!)")
}
}
So, Which is better way to achieve load earlier with smoothness same as like whatspp Application.
You should always load your data in CellForWowatIndexpath method, and also use pagination and load more data when that last cell is displayed. You can call load more method on cellWillDisplay method. And last but not least if you are loading images use SDWebimage to load the image so that the scrolling becomes smooth
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// load dats here
return cell
}
Reload the data when last cell is visible
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
let lastItem = self.YourArray.count - 1
if indexPath.row == lastItem
{
//call load more data method
}
else{
return
}
}

PFQueryTableViewController infinite-scroll swift

So what I'm trying to achieve is basically to use PFQueryTableViewController with infinite scrolling. So far, I've set paginationEnabled to false because I want to hide the "Show more" cell that appears. And I have also set the amount of posts to be loaded each "page" to be 10.
self.paginationEnabled = false
self.objectsPerPage = 10
Now for the infinite scrolling. I have set up the following function
override func scrollViewDidScroll(scrollView: UIScrollView) {
if (scrollView.contentSize.height - scrollView.contentOffset.y < (self.view.bounds.size.height)) {
if !self.loading {
self.loadNextPage()
}
}
}
The problem is that when it reaches the end (the last database rows) it just keeps looping the previous tableView cells. I need a way to check if there are new database rows to fetch or not. Any ideas?
I suggest you an idea:
Your objectsPerPag = 10. But when you query you will query 11. If you actual query return 11 objects so this result mean that continue have another object. You will continue loadmore. And when you return you will remove last object of this result query and you will get 10 item as you want.
Hope my idea can help you!
Okay, so the way I did this was that I set paginationEnabled to true instead of false.
self.paginationEnabled = true
Then I followed up the the following function
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row + 1 > self.objects?.count {
return 0
}
let height = super.tableView(tableView, heightForRowAtIndexPath: indexPath)
return height
}
What this does is that it checks for the "Load more" cell. If there are 10 objects then the 11th would be the "Load more" cell. Therefore indexPath.row + 1. It then sets its height to 0 which hides it.

Resources