4

I have looked for ways of getting the last indexPath of a UICollectionView, although below code works for a UITableView (having one section):

[NSIndexPath indexPathForRow:[yourArray count]-1 inSection:0] 

but not been able to achieve the same thing for a UICollectionView.

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
Soni A
  • 43
  • 1
  • 4
  • 3
    `[NSIndexPath indexForItem:yourArray.count-1 inSection:0]`? – Paulw11 Jul 20 '17 at 07:35
  • I think you have some error in your DataSource. Your method should work for UICollectionView, therefore a DataSource of a UICollection view is like to DataSource of a UITableView – Andrew Romanov Jul 20 '17 at 07:43
  • 1
    As @Paulw11 mentioned, collection views works with *items*, not *rows*. – Ahmad F Jul 20 '17 at 07:49
  • all i am after here is how to determine the last index path of a collectionview to use it in willdisplaycell delegate method to do a pagination rest call to the server. using post man this works and all it needs is a number being sent back to the server to fetch the next results per page – Soni A Jul 20 '17 at 08:15
  • func calculateLastIndexPath() -> IndexPath? { guard let lastPage = productPages.last else {return nil} let section = lastPage.pageNumber let item = lastPage.results.count - 1 return IndexPath(item: item, section: section) } /// Collection View Delegate fileprivate var nextPageIndex: Int { guard let lastPage = productPages.last else {return 0} return lastPage.pageNumber.advanced(by: 1) } func collectionView:willDisplayCell:forItemAtIndexPath: { if indexPath == lastIndexPath { loadProducts(page : nextPageIndex) } – Soni A Jul 20 '17 at 11:03

4 Answers4

3

You can find last index of UICollectionView like this.

    NSInteger lastSectionIndex = MAX(0, [self.yourCollectionView numberOfSections] - 1);
    NSInteger lastRowIndex = MAX(0, [self.yourCollectionView numberOfItemsInSection:lastSectionIndex] - 1);
    NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:lastRowIndex
                                                inSection:lastSectionIndex];

You can also find last index of UITableView like this.

    NSInteger lastSectionIndex = MAX(0, [self.yourTableView numberOfSections] - 1);
    NSInteger lastRowIndex = MAX(0, [self.yourTableView numberOfRowsInSection:lastSectionIndex] - 1);
    NSIndexPath *lastIndexPath = [NSIndexPath indexPathForRow:lastRowIndex
                                                inSection:lastSectionIndex];

And if you want to detect last index of a specific section, you just need to replace the index of that section with "lastSectionIndex".

Usman
  • 457
  • 4
  • 8
  • I will test this code and let you know the outcome, i am basically trying to compare the lastindexpath with the index return in the willdisplaycell delegate method, to know if i need to do a server side call for the next page of items from the server. – Soni A Jul 20 '17 at 08:21
  • This code is to get the last index of UICollectionView. If you need to make server call, please compare the "lastIndexPath" in "willDisplayCell:(UICollectionViewCell *)cell forItemAtIndexPath:(NSIndexPath *)indexPath" with current indexPath – Usman Jul 20 '17 at 08:28
  • my intention is to use this with a model of tracking the paged items from the server like so struct PageResults(let pageNumnber:Int, let items[]) – Soni A Jul 20 '17 at 08:51
  • your solution worked for me i just had to change the indexPathForRow to indexPathForItem. thanks! – Soni A Jul 20 '17 at 16:32
0

Simple way for the server side paging (that I use):

You can do the server side paging in willDisplay cell: delegate method of the collection/table view both.

You'll get the indexPath of the cell that's going to display then make a condition that will check that the showing indexPath.item is equal to the dataArray.count-1 (dataArray is an array from which your collection/table view is loaded)

bestiosdeveloper
  • 2,339
  • 1
  • 11
  • 28
  • how then will you track what page number to send to the server? if all you doing is comparing the indexPath.item == dataArray.count - 1 ? i am basically trying to do a load items on demand in a collectionview – Soni A Jul 20 '17 at 08:07
  • yes try to get the page no in api either current page no or next page no. I'll prefer for the next page no if next page is not available then send it -1, you can track end page easily form this. Or hit the next page web-service till next page count is not -1 – bestiosdeveloper Jul 20 '17 at 08:14
  • Isnt it possible to determine the next page from code rather than requesting that data from the server? – Soni A Jul 20 '17 at 08:27
  • Yes it is, but how would you know that now there is no more data available on server to show? That's why you need the page no from the server and you can send the item count to be shown in each page. – bestiosdeveloper Jul 20 '17 at 09:41
  • You simply return no results, and knowing the end page has its own impact on the back end as you will be constantly doing a count on the database tables each time, now imagine you have many clients doing the same thing, when its just easier to return a result set – Soni A Jul 20 '17 at 09:44
0

i think,
you should do it in scrollView's Delegate "scrollViewDidEndDecelerating",
check your currently visible cells by

NSArray<NSIndexPath*>* visibleCells = [self.collection indexPathsForVisibleItems];

create last indexPath by,

NSIndexPath* lastIndexPath = [NSIndexPath indexPathForItem:(datasource.count-1) inSection:0];

check conditions,

if([visibleCells containsObject:lastIndexPath]) {
            //This means you reached at last of your datasource. and here you can do load more process from server
        }

whole code will be like,
Objective C Code,

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    NSArray<NSIndexPath*>* visibleCells = [collection indexPathsForVisibleItems];
    NSIndexPath* lastIndexPath = [NSIndexPath indexPathForItem:(Blogs.count - 1) inSection:0];

    if([visibleCells containsObject:lastIndexPath]) {
        //This means you reached at last of your datasource. and here you can do load more process from server
    }
}

Swift 3.1 Code,

func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
    let visibleCells: [IndexPath] = collection.indexPathsForVisibleItems
    let lastIndexPath = IndexPath(item: (Blogs.count - 1), section: 0)
    if visibleCells.contains(lastIndexPath) {
        //This means you reached at last of your datasource. and here you can do load more process from server
    }
}
Vatsal Shukla
  • 1,274
  • 12
  • 25
  • the problem with this is, is that you will not have smooth scrolling, if you want to have a smooth experience that the user is unaware you adding more items to the collectionview, dont think this will achieve that, i might be wrong – Soni A Jul 20 '17 at 08:48
  • @SoniA , for smooth scrolling you have to use GCD ... happy coding :D – Vatsal Shukla Jul 20 '17 at 10:03
  • this is a rough sample of the code i wrote in note pad ++ as i am not at my desk but this should be enough to let you see what i am trying to do: – Soni A Jul 20 '17 at 10:59
  • func loadProducts(page Int =1, resultsPerPage:Int=0){ //api use parameters as query string to the server let products = :[Products] = parse(data) let productPage = PagedResults(pageNumber:page, results:products) //check if we have an existing product page number if we do we do not add it to the // internal list - prevents duplication guard !loadedProductPageNumners.contains(productPage.pageNumber) else {return} // append the product page to the list of product pages productPages.append(productPage) } – Soni A Jul 20 '17 at 11:05
  • func calculateLastIndexPath() -> IndexPath? { guard let lastPage = productPages.last else {return nil} let section = lastPage.pageNumber let item = lastPage.results.count - 1 return IndexPath(item: item, section: section) } /// Collection View Delegate fileprivate var nextPageIndex: Int { guard let lastPage = productPages.last else {return 0} return lastPage.pageNumber.advanced(by: 1) } func collectionView:willDisplayCell:forItemAtIndexPath: { if indexPath == lastIndexPath { loadProducts(page : nextPageIndex) } } – Soni A Jul 20 '17 at 11:06
0

Swift 5

extension UICollectionView {

    func getLastIndexPath() -> IndexPath {
    
        let lastSectionIndex = max(0, self.numberOfSections - 1)
        let lastRowIndex = max(0, self.numberOfItems(inSection: lastSectionIndex) - 1)
    
        return IndexPath(row: lastRowIndex, section: lastSectionIndex)
    }
}
Yunus T.
  • 569
  • 7
  • 13