Reputation: 5247
The code below scrolls to the right cell in UICollectionView
, but the section header view is hidden behind UINavigationBar
in my case. I believe I should use scrollRectToVisible
instead and my question is, what is the right way to calculate the CGRect
(y position) when the numberOfRows
in a given section
is variable.
- (void)scrollToPricing:(NSUInteger)row {
[self.collectionView scrollToItemAtIndexPath:
[NSIndexPath indexPathForItem:0
inSection:row]
atScrollPosition:UICollectionViewScrollPositionTop
animated:YES];
}
Upvotes: 31
Views: 24580
Reputation: 17834
Seems like all the answers are overly complex. This works for me:
let attributes = self.collectionView.collectionViewLayout.layoutAttributesForSupplementaryViewOfKind(UICollectionElementKindSectionHeader, atIndexPath: NSIndexPath(forItem: 0, inSection: section))
self.collectionView.setContentOffset(CGPointMake(0, attributes!.frame.origin.y - self.collectionView.contentInset.top), animated: true)
Swift 5:
if let attributes = collectionView.collectionViewLayout.layoutAttributesForSupplementaryView(ofKind: UICollectionView.elementKindSectionHeader, at: IndexPath(item: 0, section: section)) {
collectionView.setContentOffset(CGPoint(x: 0, y: attributes.frame.origin.y - collectionView.contentInset.top), animated: true)
}
Upvotes: 36
Reputation: 1398
Faced a similar issue initially until I realized that the UICollectionView.ScrollPosition
can be used to position the header in the collection view frame.
You just have to use the UICollectionViewScrollPositionCenteredVertically option for the header to be visible at the center of the collection view frame.
Here's how I have achieved this in Swift 4.2:
let scrollableSection = IndexPath.init(row: 0, section: indexPath.item)
emojiCollectionView.scrollToItem(at: scrollableSection, at: .centeredVertically, animated: true)
Upvotes: 1
Reputation: 20376
I found that the accepted answer no longer works properly (2 headers sticking together), especially when the collection view has to be scrolled up (down still works fine).
The following code works perfectly in either direction:
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
UICollectionViewLayoutAttributes *headerAttribs = [iconsCV layoutAttributesForSupplementaryElementOfKind:UICollectionElementKindSectionHeader atIndexPath:indexPath];
UICollectionViewLayoutAttributes *attribs = [iconsCV layoutAttributesForItemAtIndexPath:indexPath];
CGPoint topOfHeader = CGPointMake(0, attribs.frame.origin.y -collectionV.contentInset.top -headerAttribs.bounds.size.height);
[collectionV setContentOffset:topOfHeader animated:YES];
Upvotes: 1
Reputation: 3416
Based on @pixelfreak
answer
Swift 4.2
+ iOS 11 Safe Area
SUPPORT (for iPhone X and above)if let attributes = collectionView.layoutAttributesForSupplementaryElement(ofKind: UICollectionView.elementKindSectionHeader, at: IndexPath(item: 0, section: section)) {
var offsetY = attributes.frame.origin.y - collectionView.contentInset.top
if #available(iOS 11.0, *) {
offsetY -= collectionView.safeAreaInsets.top
}
collectionView.setContentOffset(CGPoint(x: 0, y: offsetY), animated: true) // or animated: false
}
HAPPY CODING
Upvotes: 11
Reputation: 1461
The easiest way is to use the section header frame.origin.x
and frame.origin.y
and then add up section header height
with the item height
to create a CGRect
to scroll to.
let sectionHeaderAttributes: UICollectionViewLayoutAttributes = self.collectionView.layoutAttributesForItem(at: scrollToIndexPath)!;
let itemAttributes: UICollectionViewLayoutAttributes = self.collectionView.layoutAttributesForSupplementaryElement(ofKind: UICollectionElementKindSectionHeader, at: scrollToIndexPath)!;
let combinedFrame: CGRect = CGRect(x: sectionHeaderAttributes.frame.origin.x, y: sectionHeaderAttributes.frame.origin.y, width: sectionHeaderAttributes.frame.width, height: sectionHeaderAttributes.frame.height + itemAttributes.frame.height);
collectionView.scrollRectToVisible(combinedFrame, animated: false);
Upvotes: 2
Reputation: 2770
// scroll to selected index
NSIndexPath* cellIndexPath = [NSIndexPath indexPathForItem:0 inSection:sectionIndex];
UICollectionViewLayoutAttributes* attr = [self.collectionView.collectionViewLayout layoutAttributesForSupplementaryViewOfKind:UICollectionElementKindSectionHeader atIndexPath:cellIndexPath];
UIEdgeInsets insets = self.collectionView.scrollIndicatorInsets;
CGRect rect = attr.frame;
rect.size = self.collectionView.frame.size;
rect.size.height -= insets.top + insets.bottom;
CGFloat offset = (rect.origin.y + rect.size.height) - self.collectionView.contentSize.height;
if ( offset > 0.0 ) rect = CGRectOffset(rect, 0, -offset);
[self.collectionView scrollRectToVisible:rect animated:YES];
Upvotes: 1
Reputation: 3394
// [myCollectionView scrollToItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:index_of_sec] atScrollPosition:UICollectionViewScrollPositionTop animated:YES];
Below code will fix your problem, as i have faced same issue and used this solution developed by me instead of above commented code.
UICollectionViewLayoutAttributes *attributes = [myCollectionView layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:index_of_sec]];
CGRect rect = attributes.frame;
[myCollectionView setContentOffset:CGPointMake(myCollectionView.frame.origin.x, rect.origin.y - HEIGHT_OF_YOUR_HEADER) animated:YES];
Upvotes: 2
Reputation: 594
First, get the frame for the header in the section:
- (CGRect)frameForHeaderForSection:(NSInteger)section {
NSIndexPath *indexPath = [NSIndexPath indexPathForItem:1 inSection:section];
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
CGRect frameForFirstCell = attributes.frame;
CGFloat headerHeight = [self collectionView:_collectionView layout:_layout referenceSizeForHeaderInSection:section].height;
return CGRectOffset(frameForFirstCell, 0, -headerHeight);
}
Note: I just put a value of 1
for the indexPath.item
. You might need to change this to something appropriate for your implementation.
Then, scroll the UIScrollView
to the point at the top of the header:
- (void)scrollToTopOfSection:(NSInteger)section animated:(BOOL)animated {
CGRect headerRect = [self frameForHeaderForSection:section];
CGPoint topOfHeader = CGPointMake(0, headerRect.origin.y - _collectionView.contentInset.top);
[_collectionView setContentOffset:topOfHeader animated:animated];
}
Note: you must subtract the contentInset
, otherwise it will be discarded and your scrollview will scroll behind the status bar and/or navigation bar.
Upvotes: 5
Reputation: 13020
I think this may help you
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];
Then you can access the location through attributes.frame
Upvotes: 11