UICollectionView automatyczne przewijanie do komórki w IndexPath

101

Przed załadowaniem widoku kolekcji użytkownik ustawia liczbę obrazów w tablicy widoku kolekcji. Nie wszystkie komórki mieszczą się na ekranie. Mam 30 ogniw i tylko 6 na ekranie.

Pytanie: jak automatycznie przewinąć do komórki z żądanym obrazem przy ładowaniu UICollectionView?

AlexanderZ
źródło

Odpowiedzi:

91

Odkryłem, że przewijanie viewWillAppearmoże nie działać niezawodnie, ponieważ widok kolekcji nie zakończył jeszcze swojego układu; możesz przewinąć do niewłaściwej pozycji.

Odkryłem również, że przewijanie viewDidAppearspowoduje, że widoczny będzie chwilowy błysk nieprzewijanego widoku.

A jeśli przewijasz za każdym razem viewDidLayoutSubviews, użytkownik nie będzie mógł ręcznie przewijać, ponieważ niektóre układy kolekcji powodują układ widoku podrzędnego za każdym razem, gdy przewijasz.

Oto, co znalazłem, działa niezawodnie:

Cel 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];
    }
}

Szybki:

 override func viewWillLayoutSubviews() {

    super.viewWillLayoutSubviews()

      if (!self.initialScrollDone) {

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

}
LenK
źródło
Niezła wskazówka. Podkreślę użycie initialScrollDoneflagi, tak jak zrobił to LenK, ponieważ ta metoda zostanie wywołana więcej niż jeden raz, a jeśli wywołasz scrollToItemAtIndexPath: więcej niż raz wydaje się, że renderuje kolekcję, której nie można przewijać (symulator iOS iPhone 5 / iOS 8)
maz
1
Działa świetnie w iOS8. Powinna być zaakceptowana odpowiedź.
Nick
5
To nie zadziałało dla mnie w iOS 8.3. Musiałem dzwonić [self.collectionView layoutIfNeeded];wcześniej, co również działa, gdy robisz to w środku viewDidLoad.
Brent Dillingham
1
To zdecydowanie powinna być akceptowana odpowiedź. Powyższa odpowiedź dała mi flash, gdy pierwsza komórka została pokazana, a następnie zaktualizowana o moją nową komórkę, ta metoda przechodzi płynnie.
simon_smiley
działa to również na iOS 9, a także nie psuje mojego automatycznego układu.
Mr. Zystem
169

Nowa, zredagowana odpowiedź:

Dodaj to viewDidLayoutSubviews

SZYBKI

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

Zwykle tak .centerVerticallyjest

ObjC

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

Stara odpowiedź działa na starszym iOS

Dodaj to w viewWillAppear:

[self.view layoutIfNeeded];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];
Boweidmann
źródło
4
To jest dokładnie to, czego próbuję użyć. Mam numer obiektu w tablicy i znam nazwę tego obiektu. Ale nie wiem, jak wstawić poprawną ścieżkę indexPath ... Jakieś pomysły?
AlexanderZ,
4
Możesz łatwo stworzyć indexPath:[NSIndexPath indexPathForItem:numberOfTheObject inSection:sectionNumber]
boweidmann
3
Wystąpiły błędy. Ale potem umieściłem cały wspomniany kod w (void) viewDidLayoutSubviews {} i teraz działa dobrze. Dzięki Bogdan.
AlexanderZ,
1
Jak mogę „powiększyć” komórkę i pokazać ją w powiększeniu?
fatuhoku
9
Nie działa to niezawodnie w systemie iOS 7.1 i może spowodować, że widok kolekcji pojawi się jako czarny ekran. Nie chciałem też utrzymywać stanu takiego jak rozwiązanie w viewDidLayoutSubviews poniżej. To, co zrobiłem, to po prostu wykonanie [self.view layoutIfNeeded] tuż przed wywołaniem scrollToItemAtIndexPath
Daniel Galasko
16

Całkowicie zgadzam się z powyższą odpowiedzią. Tyle tylko, że dla mnie rozwiązaniem był ustawiony kod w viewDidAppear

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

Kiedy użyłem viewWillAppear, przewijanie nie działało.

Raistlin
źródło
2
viewDidAppear ma tę wadę, że widzisz ruchomy / migający widok kolekcji, widok już się pojawił (jak sama nazwa wskazuje) ... Naprawdę zastanawiam się, dlaczego Apple nie zaprojektowało tego niezbyt niezwykłego przypadku w ładniejszy sposób, sposób viewDidLayoutSubviews jest trochę niezręczne
TheEye
12

to wydawało się działać dla mnie, jest podobne do innej odpowiedzi, ale ma kilka wyraźnych różnic:

- (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];
}
szklarnia
źródło
12

Szybki:

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

Szybki 3

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

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

Pozycja przewijania:

UICollectionViewScrollPosition.CenteredHorizontally / UICollectionViewScrollPosition.CenteredVertically
Rajesh Loganathan
źródło
9

Możesz użyć GCD, aby wysłać przewijanie do następnej iteracji głównej pętli uruchamiania w viewDidLoad, aby osiągnąć to zachowanie. Przewijanie zostanie wykonane przed wyświetleniem widoku kolekcji na ekranie, więc nie będzie migania.

- (void)viewDidLoad {
    dispatch_async (dispatch_get_main_queue (), ^{
        NSIndexPath *indexPath = YOUR_DESIRED_INDEXPATH;
        [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    });
}
Tianyu Wang
źródło
jak uzyskać indexpath, gdy pozycja to 3?
kemdo
5

Poleciłbym to zrobić w collectionView: willDisplay:Wtedy możesz być pewien, że delegat widoku kolekcji coś dostarcza. Oto mój przykład:

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
    }
}
Eike
źródło
1
Odkryłem, że użycie didEndDisplaying zamiast willDisplay było sposobem na poprawne działanie tego. willDisplay było w porządku, dopóki komórka była na ekranie; jednak jeśli komórki nie było jeszcze na ekranie, ta metoda nie przewijała mnie do niej. didEndDisplaying chociaż działał idealnie w tym przypadku użycia. Doceniam twoją odpowiedź, która stawia mnie na właściwej ścieżce!
C6Silver,
2

Jako alternatywa dla wyżej wymienionych. Zadzwoń po załadowaniu danych:

Szybki

collectionView.reloadData()
collectionView.layoutIfNeeded()
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .right)
nCod3d
źródło
1

Wszystkie odpowiedzi tutaj - hacki. Znalazłem lepszy sposób na przewijanie widoku kolekcji gdzieś po relaodData:
Układ widoku kolekcji podklas, jaki układ używasz, nadpisz metodę przygotowaniaLayout, po super wywołaniu ustaw wszystko, czego potrzebujesz.
np .: https://stackoverflow.com/a/34192787/1400119

trickster77777
źródło