滚动 UICollectionView 到 section header 视图

31
以下代码可以在UICollectionView中向右滚动到正确的单元格,但是在我的情况下,区域头视图被UINavigationBar遮挡了。我相信我应该使用scrollRectToVisible,我的问题是,在给定的sectionnumberOfRows是可变的时候,如何正确计算CGRect(y位置)。
- (void)scrollToPricing:(NSUInteger)row {

    [self.collectionView scrollToItemAtIndexPath:
      [NSIndexPath indexPathForItem:0 
                          inSection:row] 
                   atScrollPosition:UICollectionViewScrollPositionTop 
                           animated:YES];
}

你使用自动布局吗? - Sunny Shah
是的,我正在使用自动布局。 - Martin Koles
只需删除然后检查是否发生相同的事情? - Sunny Shah
抱歉,我不明白你的意思。我正在寻找一种方法来计算给定部分标题视图Y原点位置的内容偏移量,或者任何其他滚动集合视图的代码方式到标题视图而不是单元格。 - Martin Koles
好的,那么请看我的回答。 - Sunny Shah
9个回答

35

似乎所有的回答都过于复杂。这对我有用:

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 3:

if let attributes = collectionView.collectionViewLayout.layoutAttributesForSupplementaryView(ofKind: UICollectionElementKindSectionHeader, at: IndexPath(item: 0, section: section)) {
    collectionView.setContentOffset(CGPoint(x: 0, y: attributes.frame.origin.y - collectionView.contentInset.top), animated: true)
}

1
这个很好用。最好保护一下属性,以避免潜在的崩溃。 - Ben Thomas

11

我认为这可能对你有所帮助

UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath];

然后您可以通过attributes.frame访问位置。


这正是我所需要的。 - Martin Koles
那么,这个 scrollToItemAtIndexPath 方法是正确的吗?它没有起作用? - Sunny Shah
UICollectionViewLayoutAttributes *attributes = [self.collectionView layoutAttributesForItemAtIndexPath:indexPath]; 然后使用 attributes.frame。 - Sunny Shah
一个小时后会尝试。谢谢。 - Martin Koles
非常好,它运行良好。非常感谢。请更新答案,我会接受它。 - Martin Koles
显示剩余5条评论

11

根据@pixelfreak的回答

Swift 4.2 + iOS 11安全区域支持(适用于iPhone X及以上)

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
}

愉快的编码


5

首先,在该部分获取标题的框架:

- (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);
}

注意:我只是为 indexPath.item 设置了一个值为1。你可能需要根据你的实现更改此值为适当的值。 然后,将UIScrollView滚动到标题顶部的位置:
- (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];
}

注意:您必须减去contentInset,否则它将被丢弃并且您的滚动视图将在状态栏和/或导航栏后面滚动。

2
最简单的方法是使用“section header”的frame.origin.xframe.origin.y,然后将“section header”的高度height与“item”的高度height相加,以创建要滚动到的CGRect
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);

1
这对我来说非常完美!其他解决方案存在偏移问题,比如项目与设备边缘对齐,直到用户手动滚动,或者最后几个部分出现在屏幕顶部。这产生了我想要的确切效果。我所做的唯一区别是交换了 sectionHeaderAttributes 和 itemAttributes 的名称以匹配函数名称,但保持了 combinedFrame 不变。 - Maximilian Litteral
这对我来说也是最好的选择。它将标题部分放置在正确的偏移量处,这样当用户在tvOS上交互时就不会出现额外的自动滚动。 - Byron

2
// [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];

1
// 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];

1
我发现被接受的答案不再正常工作(2个标题粘在一起),特别是当集合视图需要向上滚动时(向下仍然可以正常工作)。
以下代码在任何方向上都可以完美地工作:
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];

0

一开始遇到了类似的问题,直到我意识到可以使用 UICollectionView.ScrollPosition 来定位集合视图框架中的标题。

您只需要使用 UICollectionViewScrollPositionCenteredVertically 选项,即可使标题在集合视图框架的中心位置可见。

以下是我在 Swift 4.2 中实现此功能的方法:

let scrollableSection = IndexPath.init(row: 0, section: indexPath.item)

emojiCollectionView.scrollToItem(at: scrollableSection, at: .centeredVertically, animated: true)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接