xcode CollectionViewController scrollToItemAtIndexPath不工作

Kar*_*Pai 30 xcode

我创建了一个CollectionView控件并用图像填充它.现在我想在开始时滚动到特定索引处的项目.我试过scrollToItemAtIndexPath如下:

[self.myFullScreenCollectionView scrollToItemAtIndexPath:indexPath 
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
Run Code Online (Sandbox Code Playgroud)

但是,我得到了以下异常.任何人都可以指导我在哪里出错.

2013-02-20 02:32:45.219 ControlViewCollection1[1727:c07] *** Assertion failure in 
-[UICollectionViewData layoutAttributesForItemAtIndexPath:], /SourceCache/UIKit_Sim/UIKit-2380.17
/UICollectionViewData.m:485 2013-02-20 02:32:45.221 ControlViewCollection1[1727:c07] must return a 
UICollectionViewLayoutAttributes instance from -layoutAttributesForItemAtIndexPath: for path 
<NSIndexPath 0x800abe0> 2 indexes [0, 4]
Run Code Online (Sandbox Code Playgroud)

fol*_*ben 74

无论是bug还是功能,UIKit都会scrollToItemAtIndexPath:atScrollPosition:Animated在调用之前调UICollectionView出此错误, 然后才会显示其子视图.

作为一种解决方法,将滚动调用移动到视图控制器生命周期中您确定已经计算其布局的位置,如下所示:

@implementation CollectionViewControllerSubclass

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    // scrolling here doesn't work (results in your assertion failure)
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];

    NSIndexPath *indexPath = // compute some index path

    // scrolling here does work
    [self.collectionView scrollToItemAtIndexPath:indexPath
                                atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally
                                        animated:YES];
}

@end
Run Code Online (Sandbox Code Playgroud)

至少,错误消息应该更有帮助.我打开了一个rdar:// 13416281 ; 请欺骗.


Cod*_*der 51

如果你想在视图控制器加载时要滚动,请确保调用layoutIfNeededUICollectionView你打电话之前scrollToItemAtIndexPath.这比将视图滚动逻辑放在viewDidLayoutSubviews中要好,因为每次布置父视图的子视图时都不会执行滚动操作.

  • 我浪费了3个多小时试图解决这个问题,你真的救了我.谢谢 (3认同)
  • 使用自动布局时,这似乎不起作用。我必须设置一个标志来指示需要挂起的滚动,然后在表视图的layoutSubviews中检查该标志(如果使用视图控制器,则检查viewDidLayoutSubview)。 (2认同)
  • 这是使用“UIViewController”生命周期的正确方法吗?我认为我们不应该在 `viewDidLoad` 中调用 `layoutIfNeeded`,因为视图控制器稍后会进行布局。即使现在它可以工作,它似乎也可能导致未来未定义的行为。 (2认同)

jws*_*art 14

有时collectionView(_:didSelectItemAt:)要么不在主线程上调用,要么阻塞它,导致scrollToItem(at:at:animated:)不做任何事情.

通过以下方式解决此问题:

DispatchQueue.main.async {
  collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
}
Run Code Online (Sandbox Code Playgroud)


Dmi*_*pov 6

你可以在viewDidLoad方法上执行此操作

只需调用preformBatchUpdates

[self performBatchUpdates:^{
        if ([self.segmentedDelegate respondsToSelector:@selector(segmentedBar:selectedIndex:)]){
            [self.segmentedDelegate segmentedBar:self selectedIndex:_selectedPage];
        }
    } completion:^(BOOL finished) {
        if (finished){
            [self scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_selectedPage inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
        }

    }];
Run Code Online (Sandbox Code Playgroud)

在我的情况下,我的子类CollectionView有一个属性selectedPage,并在我调用的这个属性的setter上

- (void)setSelectedPage:(NSInteger)selectedPage {
    _selectedPage = selectedPage;
    [self performBatchUpdates:^{
        if ([self.segmentedDelegate respondsToSelector:@selector(segmentedBar:selectedIndex:)]){
            [self.segmentedDelegate segmentedBar:self selectedIndex:_selectedPage];
        }
    } completion:^(BOOL finished) {
        if (finished){
            [self scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:_selectedPage inSection:0] atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
        }

    }];
}
Run Code Online (Sandbox Code Playgroud)

在视图控制器中我通过代码调用它

- (void)viewDidLoad {
    [super viewDidLoad];
    self.navigationBar.segmentedPages.selectedPage = 1;
}
Run Code Online (Sandbox Code Playgroud)


Wom*_*ble 6

从iOS 9.3开始,Xcode 8.2.1,Swift 3:

调用scrollToItem(at:)viewWillAppear()仍然是断开的,特别是如果你正在使用部分页眉/页脚,自动布局,科插图.

即使你打电话setNeedsLayout()layoutIfNeeded()collectionView,行为仍然是borked.将滚动代码放入动画块中无法可靠地工作.

如其他答案所示,解决方案是只有scrollToItem(at:)在确定所有内容都已布置后才会调用.即viewDidLayoutSubviews().

但是,你需要有选择性; 你不想每次viewWillLayoutSubviews()调用都要执行滚动.因此,解决方案是在其中设置标志viewWillAppear(),并在其上执行操作viewDidLayoutSubviews().

fileprivate var needsDelayedScrolling = false

override func viewWillAppear(_ animated: Bool)
{
    super.viewWillAppear(animated)
    self.needsDelayedScrolling = true
    // ...
}

override func viewDidLayoutSubviews()
{
    super.viewDidLayoutSubviews()

    if self.needsDelayedScrolling {
        self.needsDelayedScrolling = false
        self.collectionView!.scrollToItem(at: someIndexPath,
                at: .centeredVertically,
                animated: false)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*ena 5

还要记住,您需要使用正确的 UICollectionviewScrollPosition 值。请参阅以下代码以进行说明:

typedef NS_OPTIONS(NSUInteger, UICollectionViewScrollPosition) {
UICollectionViewScrollPositionNone                 = 0,

/* For a view with vertical scrolling */
// The vertical positions are mutually exclusive to each other, but are bitwise or-able with the horizontal scroll positions.
// Combining positions from the same grouping (horizontal or vertical) will result in an NSInvalidArgumentException.
UICollectionViewScrollPositionTop                  = 1 << 0,
UICollectionViewScrollPositionCenteredVertically   = 1 << 1,
UICollectionViewScrollPositionBottom               = 1 << 2,

/* For a view with horizontal scrolling */
// Likewise, the horizontal positions are mutually exclusive to each other.
UICollectionViewScrollPositionLeft                 = 1 << 3,
UICollectionViewScrollPositionCenteredHorizontally = 1 << 4,
UICollectionViewScrollPositionRight                = 1 << 5
Run Code Online (Sandbox Code Playgroud)

};