2013-02-20 92 views
21

我创建了一个CollectionView控制和处理图像填充它。现在我想在开始时滚动到特定索引处的项目。我试过scrollToItemAtIndexPath如下:的Xcode CollectionViewController scrollToItemAtIndexPath不工作

[self.myFullScreenCollectionView scrollToItemAtIndexPath:indexPath 
atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES]; 

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

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] 

回答

62

无论是错误或功能,UIKit中抛出这个错误,每当之前UICollectionView奠定了它的子视图scrollToItemAtIndexPath:atScrollPosition:Animated被调用。

作为一种变通方法,将您的滚动调用一个地方,在视图控制器生命周期中有你确定它已经计算出它的布局,像这样:

@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 

最起码,错误消息应可能会更有帮助。我已经打开rdar://13416281;请欺骗。

+1

不要忘记垂直和水平滚动的位置值:http://stackoverflow.com/a/18227362/989896 – Alena 2013-08-14 08:59:44

+1

这个答案救了我这么多发拉,谢谢!通过移动我的图像大小更新和重新加载代码到viewDidLayoutSubviews,我也摆脱了恼人的集合视图警告,所以我负债累累。 – phatmann 2013-09-26 20:48:17

+1

我把它放在viewDidApper而不是viewDidLayoutSubviews中,因为我只想滚动一次,并不是每次布局重新计算,但是你保存了我的一天。 – Vaseltior 2015-03-17 11:37:45

2

还记得,你需要使用适当的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 

};

6

U可以做到这一点,在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]; 
     } 

    }]; 

在我的情况,我的子类的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]; 
     } 

    }]; 
} 

在视图中,我通过代码调用此代码

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    self.navigationBar.segmentedPages.selectedPage = 1; 
} 
36

如果您尝试在视图控制器加载时进行滚动,请确保在致电scrollToItemAtIndexPath之前致电layoutIfNeeded,UICollectionView。这比将scroll逻辑放在viewDidLayoutSubviews中要好,因为每次布置父视图的子视图时都不会执行滚动操作。

+2

我浪费了3个多小时试图解决这个问题,你真的救了我。谢谢 – 2015-08-23 14:18:23

+0

完美!这节省了我很多时间。谢谢。 – Anand 2015-11-20 03:33:12

+1

使用自动布局时,这似乎不起作用。我必须设置一个标志,指示需要挂起的滚动,然后检查表格视图中的layoutSubviews中的该标志(如果使用视图控制器,则检查该viewDidLayoutSubview)。 – 2016-06-01 17:06:50

3

添加滚动逻辑viewDidAppear工作对我来说:

override func viewDidAppear(animated: Bool) { 
    super.viewDidAppear(animated) 
    self.collectionView?.scrollToItemAtIndexPath(
     someIndexPath, 
     atScrollPosition: UICollectionViewScrollPosition.None, 
     animated: animated) 
} 

它添加到viewDidLoad不起作用:它被忽略。

它添加到viewDidLayoutSubviews不起作用,除非你要滚动逻辑调用任何时间有什么变化。在我的情况下,从手动滚动项目

+0

高超的人!!!!! – iOSEnthusiatic 2016-12-22 17:37:06

+1

男人,你是救世主!我花了近两个小时没有任何进展。最后! – user30646 2017-04-24 03:29:35

0

夫特3

UIView.animate(withDuration: 0.4, animations: { 
    myCollectionview.scrollToItem(at: myIndexPath, at: .centeredHorizontally, animated: false) 
}, completion: { 
    (value: Bool) in 
    // put your completion stuff here 
}) 
2

作为iOS的9.3时,Xcode 8.2的防止用户。1,Swift 3:

viewWillAppear()调用scrollToItem(at:)仍然有问题,特别是如果您使用节页眉/页脚,自动布局,区域插页。

即使您在collectionView上调用setNeedsLayout()layoutIfNeeded(),行为仍然存在。将滚动代码放入动画块中并不可靠。

正如其他答案所示,解决方案只有在您确定所有内容都已经布置完毕后才致电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) 
     } 
    } 
} 
0

有时collectionView(_:didSelectItemAt:)要么不叫主线程,或阻止它,造成scrollToItem(at:at:animated:)不要做任何事情。解决此

工作做:

DispatchQueue.main.async { 
    collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true) 
} 
0

这是基于@旺布尔的回答,都归功于他们:

viewDidLayoutSubviews()被反复调用的方法。对我来说(iOS 11.2)第一次被称为collectionView.contentSize的是{0,0}。第二次,contentSize是正确的。因此,我不得不添加一个支票本:

var needsDelayedScrolling = false 

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

override func viewDidLayoutSubviews() { 
    super.viewDidLayoutSubviews() 
    if self.needsDelayedScrolling && collectionView.contentSize.width > 0 { 
     self.needsDelayedScrolling = false 
     self.collectionView!.scrollToItem(at: someIndexPath, 
       at: .centeredVertically, 
       animated: false) 
     } 
    } 
} 

增加额外的&& collectionView.contentSize.width > 0之后,它精美的作品。