11

I have an Horizontal UICollectionView on my app and I want to load more data when the user reaches the end (or nearly to the end) of UICollectionView while dragging on the left.

I'm using Swift 4. I found some Swift 3 solutions but they do not work for me.

My current code is:

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return self.videoViewModel.images.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionViewCell", for: indexPath) as! CollectionViewCell
    cell.imgImage.image = self.videoViewModel.images[indexPath.row]

    return cell
}

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    updateVideo(data: self.videoViewModel.relatedVideos[indexPath.row])
}
M1X
  • 4,971
  • 10
  • 61
  • 123

5 Answers5

21

You may use cellForItem or willDisplayItem methods of collection view. Check if the last cell is being displayed, and load your data. For example:

Swift:

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
     if (indexPath.row == dataSource.count - 1 ) { //it's your last cell
       //Load more data & reload your collection view
     }
}

Objective-C:

- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {
    if (indexPath.row == dataSource.count - 1 ) { //it's your last cell
       //Load more data & reload your collection view

    }
}
Alexandru
  • 143
  • 9
Fayza Nawaz
  • 2,256
  • 3
  • 26
  • 61
10

Implement method scrollViewDidScroll of UIScrollViewDelegate:

var isLoading: Bool = false

 func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let contentOffsetX = scrollView.contentOffset.x
    if contentOffsetX >= (scrollView.contentSize.width - scrollView.bounds.width) - 20 /* Needed offset */ {
        guard !self.isLoading else { return }
        self.isLoading = true
        // load more data
        // than set self.isLoading to false when new data is loaded
    }
}
Ilya Kharabet
  • 4,203
  • 3
  • 15
  • 29
  • 1
    I put this method on my ViewController and I also implemented UIScrollViewDelegate but nothing is happening. You can check my code at https://gitlab.com/rexhin/ios-kida.git – M1X Feb 09 '18 at 12:20
  • now it fires but multiple times like 10 or 20 if i go to the end. – M1X Feb 09 '18 at 13:16
  • 1
    Updated the answer – Ilya Kharabet Feb 09 '18 at 13:21
  • Did you set `isLoading = false` when data is loaded? – Ilya Kharabet Feb 09 '18 at 13:29
  • sorry i forgot that let my try – M1X Feb 09 '18 at 13:29
  • still not good. first it fires like 20 times than when i go to the end it fires again multiple times – M1X Feb 09 '18 at 13:36
  • Are you set `isLoading = false` on main thread? Like this: `DispatchQueue.main.async { self.isLoading = false }` – Ilya Kharabet Feb 09 '18 at 13:54
  • I can not do that because I'm using a service for that which is in another file rather than ViewController. I did it like this. `func related(videoName: String) { videoViewModel.related(videoName: videoName) { (result: VideoViewModel.Related) in switch result { case .failure(let error): print(error) case .success( _): self.collectionView.reloadData() self.isLoading = false } } } ` This is the function I call to load data. – M1X Feb 09 '18 at 14:09
  • This works in the test project. Are you sure you did everything right? Maybe you didn't remove the code from other answer? `- (void)collectionView:(UICollectionView *)collectionView willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath {...}` – Ilya Kharabet Feb 09 '18 at 14:34
  • this answer is better than the one on top, because if u have a collection view with different number of elements in each section/row the previous answer wont work. good job ilya – Jean Raymond Daher Apr 21 '20 at 13:51
  • Note: Works perfectly if you set self.collectionView.bounces = false... Otherwise, upon scrolling to the bottom, the collection view will bounce and another cell that's not last will display – Joshua Wolff Jul 27 '20 at 22:06
1

Take one bool variable and one integer variable for page number like this:

var isDataLoading = false
var pageCount:Int = 1 // Pass this page number in your api

in cellForItemAt method add below code:

if !isDataLoading && indexPath.row == videoViewModel.count - 1 {
            isDataLoading = true
            pageCount += 1
// add you api call code here with pageCount
        }

once you get your data from api set isDataLoading bool to false like below:

self.isDataLoading = false
iVarun
  • 6,496
  • 2
  • 26
  • 34
0

This is simple login which will go down to last index...!!!

func ScrollEnd() {
          let lastSectionIndex = (self.collectionView?.numberOfSections)! - 1
         let lastItemIndex = (self.collectionView.numberOfItems(inSection: lastSectionIndex)) - 1
           let index =  IndexPath(item: lastItemIndex, section: lastSectionIndex)
           if messages.count != 0{
               self.collectionView!.scrollToItem(at: index, at: UICollectionView.ScrollPosition.bottom, animated: false)
           }    }
Zain Shahzad
  • 74
  • 1
  • 4
0

Add this in your collectionView willDisplayCell delegate

if (indexPath.row == dataSource.count - 1 ) {
 //it's your last cell

//Load more data & reload your collection view
  }
Inder
  • 3,711
  • 9
  • 27
  • 42