Peter Lapisu
Peter Lapisu

Reputation: 20975

UICollectionView scroll to any footer or header view

I want to scroll to footer or header view of the collection view, however, the standard approach with scrollToItemAtIndexPath scrolls only to the cells

- (void)scrollToBottom {
        NSInteger section = [self numberOfSectionsInCollectionView:self.collectionView] - 1;
        NSInteger item = [self collectionView:self.collectionView numberOfItemsInSection:section] - 1;
        if ((section > 0) && (item > 0)) {
            NSIndexPath * lastIndexPath = [NSIndexPath indexPathForItem:item inSection:section];
            [self.collectionView scrollToItemAtIndexPath:lastIndexPath atScrollPosition:UICollectionViewScrollPositionBottom animated:NO];
        }
}

How to scroll to any footer, header view, similar as with scrolling to cell?

Upvotes: 9

Views: 4215

Answers (3)

Aymeric Baur
Aymeric Baur

Reputation: 91

I recently had the same issue. Here is my solution in Swift 4 to mimic the behavior of scrollToItem(at:at:animated:) but with Headers and Footers. Feel free to improve:

extension UICollectionView {
    func scrollToSupplementaryView(ofKind kind: String, indexPath: IndexPath, scrollPosition: UICollectionView.ScrollPosition, animated: Bool) {
        layoutIfNeeded()
        if let layoutAttributes = layoutAttributesForSupplementaryElement(ofKind: kind, at: indexPath) {
            let viewOrigin = CGPoint(x: layoutAttributes.frame.origin.x, y: layoutAttributes.frame.origin.y)
            var resultOffset: CGPoint = contentOffset

            switch scrollPosition {
            case UICollectionView.ScrollPosition.top:
                resultOffset.y = viewOrigin.y - contentInset.top

            case UICollectionView.ScrollPosition.left:
                resultOffset.x = viewOrigin.x - contentInset.left

            case UICollectionView.ScrollPosition.right:
                resultOffset.x = (viewOrigin.x - contentInset.left) - (frame.size.width - layoutAttributes.frame.size.width)

            case UICollectionView.ScrollPosition.bottom:
                resultOffset.y = (viewOrigin.y - contentInset.top) - (frame.size.height - layoutAttributes.frame.size.height)

            case UICollectionView.ScrollPosition.centeredVertically:
                resultOffset.y = (viewOrigin.y - contentInset.top) - (frame.size.height / 2 - layoutAttributes.frame.size.height / 2)

            case UICollectionView.ScrollPosition.centeredHorizontally:
                resultOffset.x = (viewOrigin.x - contentInset.left) - (frame.size.width / 2 - layoutAttributes.frame.size.width / 2)
            default:
                break
            }
            scrollRectToVisible(CGRect(origin: resultOffset, size: frame.size), animated: animated)
        }
    }
}

Gist available here: https://gist.github.com/aymericbaur/3256e25e33b64c3e5d380febf832db07

I hope it helps.

Upvotes: 9

Liviu R
Liviu R

Reputation: 689

Old question , but still relevant one as it seems. For me - @mojoTosh's solution was not working but the following was: (Swift 4)

 if let attributes = self.collectionView?.layoutAttributesForSupplementaryElement(ofKind: UICollectionElementKindSectionHeader, at: indexPath),
                        let cellAttributes = self.collectionView?.layoutAttributesForItem(at: indexPath) {
                        let scrollingPosition = CGPoint(x: 0.0, y: cellAttributes.frame.origin.y - attributes.frame.size.height - collectionView.contentInset.top)
                        self.collectionView?.setContentOffset(scrollingPosition, animated: true)

Upvotes: 0

MojoTosh
MojoTosh

Reputation: 1886

I know this is an old question now, but I recently had the same issue. The best solution I found was from Gene De Lisa at http://www.rockhoppertech.com/blog/scroll-to-uicollectionview-header/ As you appear to be working in Obj-C, here's the port of his Swift code that I use:

-(void) scrollToSectionHeader:(int)section {    
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:section];
    UICollectionViewLayoutAttributes *attribs = [self.collectionView layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
    CGPoint topOfHeader = CGPointMake(0, attribs.frame.origin.y - self.collectionView.contentInset.top);
    [self.collectionView setContentOffset:topOfHeader animated:YES];
}

The code above will properly scroll to a given section's header (all I needed). It's trivial to modify this to scroll to a footer instead:

-(void) scrollToSectionFooter:(int)section {    
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:section];
    UICollectionViewLayoutAttributes *attribs = [self.collectionView layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionFooter atIndexPath:indexPath];
    CGPoint topOfFooter = CGPointMake(0, attribs.frame.origin.y - self.collectionView.contentInset.top);
    [self.collectionView setContentOffset:topOfFooter animated:YES];
}

Upvotes: 9

Related Questions