Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect scrolling to a new section in a UICollectionView?

I am implementing an infinite-scrolling calendar. My issue is that I would like to set the current month as the title in the navigation bar and it should update while scrolling - once you pass the section header view the title should update in the nav bar.

A possible solution would be to set the view title in the method called - (UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath so that, when I calculate a new section Header, it also updates the title. The problem with this is that the title changes when the new section is at the bottom of the page.

Currently displaying the Section Header as title, not optimal

Is there a way to know the "current section" of UICollectionView once the user has scrolled to it? Or can you think of a way to improve my current solution?

To help the readers of this post, I posted my own sample code for this question at this GitHub repo.

like image 912
maggix Avatar asked Jun 14 '14 21:06

maggix


People also ask

How do I find scrolling collection view?

So if you have set the delegate and implemented UIScrollViewDelegate , you should be able to detect this the same way as UIScrollView . - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; As per documentation, the above method should tell when the scroll view has ended decelerating the scrolling movement.

Is CollectionView scrollable?

CollectionView defines two ScrollTo methods, that scroll items into view.

What is swift Uicollectionview?

An object that manages an ordered collection of data items and presents them using customizable layouts.


1 Answers

I have been pondering an algorithm that would allow you to know when the user has scrolled past a section header in order to update the title, and after some experimentation I have figured out how to implement the desired behavior.

Essentially, every time the scroll position changes you need to know what section the user is on and update the title. You do this via scrollViewDidScroll on the UIScrollViewDelegate - remembering a collection view is a scroll view. Loop over all the headers and find the one that's closest to the current scroll position, without having a negative offset. To do that, I utilized a property that stores an array of each section header's position. When a header is created, I store its position in the array at the appropriate index. Once you've found the header that's closest to your scroll position (or the index location of said header), simply update the title in the navigation bar with the appropriate title.

In viewDidLoad, fill the array property with NSNull for each section you have:

self.sectionHeaderPositions = [[NSMutableArray alloc] init];
for (int x = 0; x < self.sectionTitles.count; x++) {
    [self.sectionHeaderPositions addObject:[NSNull null]];
}

In collectionView:viewForSupplementaryElementOfKind:atIndexPath:, update the array with the position of the created header view:

NSNumber *position = [NSNumber numberWithFloat:headerView.frame.origin.y + headerView.frame.size.height];
[self.sectionHeaderPositions replaceObjectAtIndex:indexPath.section withObject:position];

In scrollViewDidScroll:, perform the calculations to determine which title is appropriate to display for that scroll position:

CGFloat currentScrollPosition = self.collectionView.contentOffset.y + self.collectionView.contentInset.top;
CGFloat smallestPositiveHeaderDifference = CGFLOAT_MAX;
int indexOfClosestHeader = NSNotFound;

//find the closest header to current scroll position (excluding headers that haven't been reached yet)
int index = 0;
for (NSNumber *position in self.sectionHeaderPositions) {
    if (![position isEqual:[NSNull null]]) {
        CGFloat floatPosition = position.floatValue;
        CGFloat differenceBetweenScrollPositionAndHeaderPosition = currentScrollPosition - floatPosition;
        if (differenceBetweenScrollPositionAndHeaderPosition >= 0 && differenceBetweenScrollPositionAndHeaderPosition <= smallestPositiveHeaderDifference) {
            smallestPositiveHeaderDifference = differenceBetweenScrollPositionAndHeaderPosition;
            indexOfClosestHeader = index;
        }
    }
    index++;
}
if (indexOfClosestHeader != NSNotFound) {
    self.currentTitle.text = self.sectionTitles[indexOfClosestHeader];
} else {
    self.currentTitle.text = self.sectionTitles[0];
}

This will correctly update the title in the nav bar once the user scrolls past the header for a section. If they scroll back up it will update correctly as well. It also correctly sets the title when they haven't scrolled past the first section. It however doesn't handle rotation very well. It also won't work well if you have dynamic content, which may cause the stored positions of the header views to be incorrect. And if you support jumping to a specific section, the user jumps to a section whose previous section's section header hasn't been created yet, and that section isn't tall enough such that the section header is underneath the nav bar (the last section perhaps), the incorrect title will be displayed in the nav bar.

If anyone can improve upon this to make it more efficient or otherwise better please do and I'll update the answer accordingly.

like image 70
Jordan H Avatar answered Sep 21 '22 04:09

Jordan H