UICollectionView scorre automaticamente alla cella in IndexPath


101

Prima di caricare la vista della raccolta, l'utente imposta il numero di immagini nella matrice della vista della raccolta. Tutte le celle non si adattano allo schermo. Ho 30 celle e solo 6 sullo schermo.

La domanda: come scorrere automaticamente fino alla cella con l'immagine desiderata al caricamento di UICollectionView?

Risposte:


90

Ho scoperto che lo scorrimento viewWillAppearpotrebbe non funzionare in modo affidabile perché la vista della raccolta non ha ancora terminato il suo layout; puoi scorrere fino all'elemento sbagliato.

Ho anche scoperto che lo scorrimento viewDidAppearrenderà visibile un flash momentaneo della vista senza scorrimento.

E, se scorri ogni volta viewDidLayoutSubviews, l'utente non sarà in grado di scorrere manualmente perché alcuni layout di raccolta causano il layout di sottoview ogni volta che scorri.

Ecco cosa ho trovato che funziona in modo affidabile:

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

Swift:

 override func viewWillLayoutSubviews() {

    super.viewWillLayoutSubviews()

      if (!self.initialScrollDone) {

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

}

Bel suggerimento. Sottolineerò l'utilizzo di un initialScrollDoneflag come ha fatto LenK poiché questo metodo verrà chiamato più di una volta e se chiami scrollToItemAtIndexPath: più di una volta sembra rendere la collectionView non scorrevole (simulatore iOS iPhone 5 / iOS 8)
maz

1
Funziona alla grande in iOS8. Dovrebbe essere la risposta accettata.
Nick

5
Questo non ha funzionato per me in iOS 8.3. Ho dovuto chiamare [self.collectionView layoutIfNeeded];prima, il che funziona anche quando lo fai all'interno viewDidLoad.
Brent Dillingham

1
Questa dovrebbe essere sicuramente la risposta accettata. La risposta sopra mi ha dato un lampo mentre la prima cella è stata mostrata e poi aggiornata con la mia nuova cella, questo metodo si transita senza problemi.
simon_smiley

funziona anche su iOS 9 e inoltre non rovina il mio layout automatico.
Mr. Zystem

168

Risposta nuova, modificata:

Aggiungi questo viewDidLayoutSubviews

SWIFT

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

Normalmente .centerVerticallyè così

objC

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

Vecchia risposta funzionante per iOS meno recenti

Aggiungi questo in viewWillAppear:

[self.view layoutIfNeeded];
[self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredVertically animated:NO];

4
È esattamente quello che sto cercando di usare. Ho il numero dell'oggetto nell'array e conosco il nome di quell'oggetto. Ma non so come mettere indexPath corretto ... Qualche idea?
AlexanderZ

4
Puoi facilmente creare indexPath:[NSIndexPath indexPathForItem:numberOfTheObject inSection:sectionNumber]
boweidmann

3
Ha avuto degli errori. Ma poi ho messo tutto il codice menzionato in (void) viewDidLayoutSubviews {} e ora funziona bene. Grazie Bogdan.
AlexanderZ

1
Come posso "ingrandire" la cella e visualizzarla ingrandita?
fatuhoku

9
Questo non funziona in modo affidabile su iOS 7.1 e può far apparire la visualizzazione della raccolta come una schermata nera. Inoltre, non volevo mantenere lo stato come la soluzione in viewDidLayoutSubviews di seguito. Quello che ho fatto è stato semplicemente eseguito un [self.view layoutIfNeeded] subito prima della chiamata a scrollToItemAtIndexPath
Daniel Galasko

15

Sono totalmente d'accordo con la risposta di cui sopra. L'unica cosa è che per me la soluzione è stata impostare il codice in viewDidAppear

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

Quando ho usato viewWillAppear lo scorrimento non funzionava.


2
viewDidAppear ha lo svantaggio di vedere la vista della raccolta muoversi / lampeggiare, la vista è già apparsa (come suggerisce il nome) ... Mi chiedo davvero perché Apple non abbia progettato questo caso non così insolito in un modo più carino, il modo viewDidLayoutSubviews lo è un po 'imbarazzante
TheEye

12

questo sembrava funzionare per me, è simile a un'altra risposta ma presenta alcune differenze distinte:

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

12

Swift:

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

Swift 3

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

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

Posizione di scorrimento:

UICollectionViewScrollPosition.CenteredHorizontally / UICollectionViewScrollPosition.CenteredVertically

9

Puoi utilizzare GCD per inviare lo scorrimento all'iterazione successiva del ciclo di esecuzione principale in viewDidLoad per ottenere questo comportamento. Lo scorrimento verrà eseguito prima che la vista della raccolta venga mostrata sullo schermo, quindi non ci sarà alcun lampeggiamento.

- (void)viewDidLoad {
    dispatch_async (dispatch_get_main_queue (), ^{
        NSIndexPath *indexPath = YOUR_DESIRED_INDEXPATH;
        [self.collectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:NO];
    });
}

come ottenere indexpath quando la posizione è 3?
kemdo

5

Consiglierei di farlo in collectionView: willDisplay:Quindi puoi essere sicuro che il delegato della visualizzazione della raccolta fornisce qualcosa. Ecco il mio esempio:

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

1
Ho scoperto che usare didEndDisplaying, invece di willDisplay, era il modo per farlo funzionare correttamente. willDisplay andava bene finché la cella era sullo schermo; tuttavia, se la cella non era ancora sullo schermo, questo metodo non mi ha fatto scorrere fino ad essa. didEndDisplaying ha funzionato perfettamente per quel caso d'uso. Apprezzo la tua risposta che mi ha messo sulla strada giusta!
C6 Silver

2

In alternativa a quanto sopra menzionato. Chiamata dopo il caricamento dei dati:

veloce

collectionView.reloadData()
collectionView.layoutIfNeeded()
collectionView.selectItem(at: indexPath, animated: true, scrollPosition: .right)

1

Tutte le risposte qui - hack. Ho trovato un modo migliore per scorrere la vista della raccolta da qualche parte dopo relaodData:
layout della vista della raccolta sottoclasse che mai layout usi, sovrascrivi il metodo prepareLayout, dopo la super chiamata imposta ciò che ti serve.
es: https://stackoverflow.com/a/34192787/1400119

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.