滚动 UICollectionView 到部分标题视图

Posted

技术标签:

【中文标题】滚动 UICollectionView 到部分标题视图【英文标题】:Scroll UICollectionView to section header view 【发布时间】:2014-02-28 16:13:03 【问题描述】:

下面的代码滚动到UICollectionView 中的右侧单元格,但在我的例子中,部分标题视图隐藏在UINavigationBar 后面。我相信我应该改用scrollRectToVisible,我的问题是,当给定section 中的numberOfRows 是可变的时,计算CGRect(y 位置)的正确方法是什么。

- (void)scrollToPricing:(NSUInteger)row 

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

【问题讨论】:

你在使用自动布局吗? 是的,我正在使用自动布局。 只是删除然后检查是否发生了同样的事情? 对不起,不明白你的意思。我正在寻找一种方法来计算给定节标题视图 Y 原点位置的内容偏移量,或者以任何其他方式将代码中的集合视图滚动到标题视图而不是单元格。 好的,然后看看我的回答 【参考方案1】:

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

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)

斯威夫特 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)

【讨论】:

这很好用。最好保护 let 属性以避免潜在的崩溃。【参考方案2】:

我觉得这对你有帮助

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

然后就可以通过attributes.frame访问该位置

【讨论】:

我有更多部分,根据收藏视图左侧菜单上的点击,我需要滚动到收藏视图顶部的所需部分。我需要在这个 [self.collectionView scrollRectToVisible:CGRectMake(0, headerOffset, self.collectionView.frame.size.width, self.collectionView.frame.size.height) animated:YES]; 中获取 headerOffset 表示你有多个部分,你想在特定部分滚动? 这正是我需要的 那么这个scrollToItemAtIndexPath方法是对的还是不行? 阅读问题。它正在工作,但在使用时,它会滚动到该项目。不是项目的节标题视图的顶部边缘。我基本上需要滚动到部分中第一项减去标题高度的点上方。【参考方案3】:

基于@pixelfreak的回答

Swift 4.2 + ios 11 Safe Area 支持(适用于 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

快乐编码

【讨论】:

【参考方案4】:

首先,获取section中header的frame:

- (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,否则它将被丢弃,并且您的滚动视图将滚动到状态栏和/或导航栏后面。

【讨论】:

【参考方案5】:

最简单的方法是使用 section header frame.origin.xframe.origin.y 然后将 section header heightitem 相加strong> 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);

【讨论】:

这对我来说很完美!其他解决方案存在偏移问题,例如项目与设备边缘对齐,直到用户手动滚动,或者最后几个部分出现在屏幕顶部。这产生了我想要的确切效果。我所做的唯一区别是交换 sectionHeaderAttributes 和 itemAttributes 的名称以匹配函数名称,但保持 combineFrame 相同。 这对我来说也是最好的。它将标题部分放在正确的偏移量处,这样当用户在 tvOS 上交互时就不会出现额外的自动滚动。【参考方案6】:
// [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];

【讨论】:

【参考方案7】:
// 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];

【讨论】:

【参考方案8】:

我发现接受的答案不再正常工作(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];

【讨论】:

【参考方案9】:

最初遇到了类似的问题,直到我意识到UICollectionView.ScrollPosition 可用于在集合视图框架中定位标题。

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

这是我在 Swift 4.2 中实现这一目标的方法:

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

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

【讨论】:

以上是关于滚动 UICollectionView 到部分标题视图的主要内容,如果未能解决你的问题,请参考以下文章

以编程方式滚动到 UICollectionView 中的补充视图

滚动到 UICollectionView 中的下一个单元格

UICollectionView 的每个部分的独立滚动?

滚动条错误地出现在 UICollectionView 部分标题下方

即使我们滚动,如何将部分标题保持在顶部:UICollectionView

每个部分的 UICollectionView 布局