2013-02-20 13 views
21

Ich habe eine CollectionView Control erstellt und mit Bildern gefüllt. Jetzt möchte ich bei einem bestimmten Index beim Start zu einem Gegenstand blättern. Ich habe scrollToItemAtIndexPath ausprobiert wie folgt:xcode CollectionViewController scrollToItemAtIndexPath funktioniert nicht

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

Aber ich bin immer folgende Ausnahme. Kann mir jemand sagen, wo ich falsch liege?

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] 

Antwort

62

Ob es ein Bug oder ein Feature, wirft UIKit diesen Fehler, wenn scrollToItemAtIndexPath:atScrollPosition:Animated aufgerufen wird, bevor UICollectionView seinen Subviews gelegt hat.

Als Abhilfe können Sie Ihre Scrollen Aufruf im View-Controller-Lebenszyklus an einen Ort ziehen, wo Sie sicher sind es bereits ist, berechnet es ist das Layout, etwa so:

@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 

Zumindest sollte die Fehlermeldung wahrscheinlich hilfreicher sein. Ich habe eine rdar://13416281 geöffnet; Bitte betrügen.

+1

Vergessen Sie nicht die Positionswerte für vertikales und horizontales Scrollen: http://StackOverflow.com/a/18227362/989896 – Alena

+1

Diese Antwort hat mich so viel Haar-ziehen gerettet, danke! Durch das Verschieben meiner Bildgrößenaktualisierung und das erneute Laden von Code in viewDidLayoutSubviews wurde auch eine lästige Sammlungsansichtswarnung gelöscht, sodass ich doppelt verschuldet bin. – phatmann

+1

Ich habe es in der ViewDidApper anstelle der ViewDidLayoutSubviews, da ich es nur einmal scrollen möchte, nicht jedes Mal, wenn das Layout neu berechnet wird, aber Sie meinen Tag gespeichert. – Vaseltior

2

Denken Sie auch daran, dass Sie den richtigen UICollectionviewScrollPosition-Wert verwenden müssen. Bitte zur Verdeutlichung den folgenden Code beachten:

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 kann dies tun und auf viewDidLoad Methode

gerade Anruf 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]; 
     } 

    }]; 

In meinem Fall meine Unterklasse Collection hat eine Eigenschaft selectedPage, und auf einem Einrichter dieser Eigenschaft nennen i

In Sicht Controller ruft ich dies durch Code

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

Wenn Sie versuchen, einen Bildlauf durchzuführen, wenn der Ansichtscontroller geladen wird, rufen Sie unbedingt layoutIfNeeded unter UICollectionView auf, bevor Sie scrollToItemAtIndexPath anrufen. Dies ist besser als die Scroll-Logik in viewDidLayoutSubviews zu setzen, da Sie den Scroll-Vorgang nicht jedes Mal ausführen, wenn die Subviews der übergeordneten Ansicht angelegt werden.

+2

Ich verschwendete mehr als 3 Stunden, um dieses Problem zu lösen, du hast mich wirklich gerettet. danke –

+0

Perfekt! Das hat mir viel Zeit gespart. Vielen Dank. – Anand

+1

Dies scheint nicht zu funktionieren, wenn das automatische Layout verwendet wird. Ich musste ein Flag setzen, das anzeigte, dass ein ausstehender Bildlauf erforderlich war, und dann dieses Flag in layoutSubviews in einer Tabellenansicht überprüfen (oder viewDidLayoutSubview, wenn ein View-Controller verwendet wurde). –

3

die Scrolling-Logik zu viewDidAppear Hinzufügen arbeitete für mich:

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

es viewDidLoad Hinzufügen funktioniert nicht: es ignoriert wird.

Hinzufügen zu viewDidLayoutSubviews funktioniert nicht, es sei denn, Sie möchten die Logik aufrufen, wenn sich etwas ändert. In meinem Fall verhindert, dass der Benutzer das Element manuell scrollen

+0

Hervorragender Mann !!!!! – iOSEnthusiatic

+1

Mann, du bist der Retter! Ich habe fast zwei Stunden ohne Fortschritte verbracht. Endlich! – user30646

0

Swift 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

Ab iOS 9.3, Xcode 8.2.1, Swift 3:

Das Aufrufen von scrollToItem(at:) von viewWillAppear() ist immer noch nicht erfolgreich, insbesondere wenn Sie Abschnittsüberschriften/Fußzeilen, Auto-Layout, Abschnittsinvertierungen verwenden.

Auch wenn Sie setNeedsLayout() und layoutIfNeeded() auf der collectionView aufrufen, ist das Verhalten noch borked. Das Einfügen des Scroll-Codes in einen Animationsblock funktioniert nicht zuverlässig.

Wie in den anderen Antworten angegeben, ist die Lösung, nur scrollToItem(at:) zu rufen, sobald Sie sicher sind, dass alles ausgelegt wurde. d.h. in viewDidLayoutSubviews().

Sie müssen jedoch selektiv sein; Sie möchten nicht jedes Mal scrollen, wenn viewWillLayoutSubviews() aufgerufen wird. Eine Lösung besteht also darin, ein Flag in viewWillAppear() zu setzen und es in viewDidLayoutSubviews() darauf zu setzen.

d.h.

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

Manchmal collectionView(_:didSelectItemAt:) entweder nicht auf dem Haupt-Thread genannt, oder blockiert es, wodurch scrollToItem(at:at:animated:) etwas nicht zu tun.

Umgehen dies tun:

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

Diese auf @ Antwort der Womble basiert, alle Kredit ihnen geht:

Verfahren viewDidLayoutSubviews() wiederholt aufgerufen wird. Für mich (iOS 11.2) heißt das erste Mal collectionView.contentSize{0,0}. Das zweite Mal ist die contentSize korrekt. Deshalb musste ich dafür einen Scheck hinzuzufügen:

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) 
     } 
    } 
} 

Nach dem Hinzufügen, dass zusätzliche && collectionView.contentSize.width > 0 es schön funktioniert.

Verwandte Themen