UICollectionView自动滚动到IndexPath的单元格


101

在加载收集视图之前,用户需要在收集视图的数组中设置图像数。屏幕上没有所有的单元格。我有30个单元格,屏幕上只有6个单元格。

问题:如何在加载UICollectionView时自动滚动到具有所需图像的单元格?

Answers:


90

我发现,滚动滚动viewWillAppear可能无法可靠地进行,因为集合视图尚未完成其布局。您可能滚动到错误的项目。

我还发现,滚动viewDidAppear将使未滚动视图的瞬间闪烁可见。

并且,如果您每次滚动viewDidLayoutSubviews,用户将无法手动滚动,因为某些集合布局会在每次滚动时导致子视图布局。

这是我发现可靠工作的结果:

目标C:

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    // If we haven't done the initial scroll, do it once.
    if (!self.initialScrollDone) {
        self.initialScrollDone = YES;

        [self.collectionView scrollToItemAtIndexPath:self.myInitialIndexPath
                                    atScrollPosition:UICollectionViewScrollPositionRight animated:NO];
    }
}

斯威夫特:

 override func viewWillLayoutSubviews() {

    super.viewWillLayoutSubviews()

      if (!self.initialScrollDone) {

         self.initialScrollDone = true
         self.testNameCollectionView.scrollToItem(at:selectedIndexPath, at: .centeredHorizontally, animated: true)
    }

}

不错的提示。我会强调使用initialScrollDoneLenK所做的标记,因为此方法将被多次调用,并且如果您调用scrollToItemAtIndexPath:不止一次,它似乎会使collectionView不可滚动(iOS模拟器iPhone 5 / iOS 8)
maz

1
在iOS8中效果很好。应该是公认的答案。
尼克

5
这在iOS 8.3中对我不起作用。必须先致电[self.collectionView layoutIfNeeded];,在内也可以使用viewDidLoad
布伦特·迪林汉姆

1
这绝对应该是公认的答案。上面的答案让我眼前一亮,显示了第一个单元格,然后用我的新单元格进行了更新,此方法可以无缝过渡。
simon_smiley 2015年

这也适用于iOS 9,也不会弄乱我的自动布局。
Zystem先生,2015年

168

新增,修改后的答案:

在此添加 viewDidLayoutSubviews

迅速

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()
    let indexPath = IndexPath(item: 12, section: 0)
    self.collectionView.scrollToItem(at: indexPath, at: [.centeredVertically, .centeredHorizontally], animated: true)
}

通常.centerVertically是这样

对象

-(void)viewDidLayoutSubviews {
   [super viewDidLayoutSubviews];
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:12 inSection:0];
   [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically | UICollectionViewScrollPositionCenteredHorizontally animated:NO];
}

适用于旧iOS的旧答案

将此添加到viewWillAppear

[self.view layoutIfNeeded];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];

4
这正是我要使用的。我在数组中有对象的编号,并且知道该对象的名称。但是不知道如何放置正确的indexPath ...有什么想法吗?
AlexanderZ

4
您可以轻松创建indexPath[NSIndexPath indexPathForItem:numberOfTheObject inSection:sectionNumber]
boweidmann

3
发生了一些错误。但随后,我将所有提到的代码放入(void)viewDidLayoutSubviews {}中,现在一切正常。Thanx Bogdan。
AlexanderZ

1
我如何“放大”到单元格中并放大显示?
fatuhoku 2014年

9
这在iOS 7.1上无法可靠运行,并且可能导致收藏夹视图显示为黑屏。我也不想像下面的viewDidLayoutSubviews中那样维护状态。我所做的只是在对scrollToItemAtIndexPath进行调用之前执行了[self.view layoutIfNeeded]
Daniel Galasko 2014年

15

我完全同意以上答案。唯一的问题是,对我来说,解决方案是在viewDidAppear中设置代码

viewDidAppear 
{
    [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:YES];
}

当我使用viewWillAppear时,滚动不起作用。


2
viewDidAppear的缺点是您看到集合视图正在移动/闪烁,该视图已经出现(顾名思义)...我真的很奇怪,为什么Apple没有以一种更好的方式设计这种不寻常的情况,viewDidLayoutSubviews方法是有点尴尬
TheEye

12

这似乎对我有用,它类似于另一个答案,但有一些明显的不同:

- (void)viewDidLayoutSubviews
{     
   [self.collectionView layoutIfNeeded];
   NSArray *visibleItems = [self.collectionView indexPathsForVisibleItems];
   NSIndexPath *currentItem = [visibleItems objectAtIndex:0];
   NSIndexPath *nextItem = [NSIndexPath indexPathForItem:someInt inSection:currentItem.section];

   [self.collectionView scrollToItemAtIndexPath:nextItem atScrollPosition:UICollectionViewScrollPositionNone animated:YES];
}

12

迅速:

your_CollectionView.scrollToItemAtIndexPath(indexPath, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: true)

迅捷3

let indexPath = IndexPath(row: itemIndex, section: sectionIndex)

collectionView.scrollToItem(at: indexPath, at: UICollectionViewScrollPosition.right, animated: true)

滚动位置:

UICollectionViewScrollPosition.CenteredHorizontally / UICollectionViewScrollPosition.CenteredVertically

9

您可以使用GCD将滚动条分派到viewDidLoad中的主运行循环的下一个迭代中,以实现此行为。滚动将在屏幕上显示集合视图之前执行,因此不会闪烁。

- (void)viewDidLoad {
    dispatch_async (dispatch_get_main_queue (), ^{
        NSIndexPath *indexPath = YOUR_DESIRED_INDEXPATH;
        [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    });
}

位置为3时如何获取indexpath?
kemdo '18

5

我建议在中进行操作。collectionView: willDisplay:然后,您可以确保集合视图委托提供了某些东西。这是我的例子:

func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    /// this is done to set the index on start to 1 to have at index 0 the last image to have a infinity loop
    if !didScrollToSecondIndex {
        self.scrollToItem(at: IndexPath(row: 1, section: 0), at: .left, animated: false)
        didScrollToSecondIndex = true
    }
}

1
我发现使用didEndDisplaying代替willDisplay是使其正常工作的方法。只要单元格在屏幕上,willDisplay就可以了;但是,如果该单元格尚未出现在屏幕上,则此方法不会将我滚动到该单元格上。didEndDisplaying虽然在该用例中效果很好。感谢您的回答,让我走上正确的道路!
C6Silver

2

作为上述替代。数据加载后调用:

迅速

collectionView.reloadData()
collectionView.layoutIfNeeded()
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .right)

1

所有答案都在这里-骇客。我发现了在relaodData之后将集合视图滚动到某个位置的更好方法:
子类集合视图布局使用的布局,重写方法prepareLayout,在超级调用后设置所需的偏移量。
例如:https//stackoverflow.com/a/34192787/1400119

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.