2013-02-20 58 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之後,它精美的作品。