web-dev-qa-db-fra.com

Démarrer UICollectionView à un chemin d'index spécifique

J'ai actuellement une vue de collection qui effectue une pagination horizontale où chaque cellule est en plein écran. Ce que je veux faire, c'est que la collectionview commence à un index spécifique quand il apparaît. 

J'utilise actuellement scrollToItemAtIndexPath: atScrollPosition: animated: avec l'animation définie sur NO, mais charge toujours le premier index avant de pouvoir accéder à l'élément en question. Il semble également que je ne puisse utiliser cette méthode que dans ViewDidAppear, de sorte qu'elle affiche la première cellule, puis clignote sur la cellule que je souhaite afficher. Je masque cela en masquant la vue de la collection jusqu'à la fin du parchemin, mais cela ne semble pas idéal. 

Y a-t-il une meilleure façon de faire cela autre que celle que j'ai décrite? 

Merci!

34
Zack

J'ai donc résolu ce problème d'une manière différente, en utilisant la méthode UICollectionViewDelegate et une méthode unique Bool:

Swift 2:

var onceOnly = false

internal func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
    if !onceOnly {
        let indexToScrollTo = NSIndexPath(forRow: row, inSection: section)
        self.problemListCollectionView.scrollToItemAtIndexPath(indexToScrollTo, atScrollPosition: .Left, animated: false)
        onceOnly = true
    }

}

Swift 3:

  var onceOnly = false

  internal func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
    if !onceOnly {
      let indexToScrollTo = IndexPath(item: row, section: section)
      self.problemListCollectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
      onceOnly = true
    }
  }

Ce code est exécuté avant toute animation (donc il se charge vraiment jusqu'à maintenant), ce qui est mieux que d'essayer d'appeler viewDidAppear et que je n'ai pas eu de succès avec viewWillAppear.

51
sschale

Pour résoudre ce problème, j’ai partiellement utilisé la réponse serre .

/// Edit
var startIndex: Int! = 0

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(animated)

    collectionView.setNeedsLayout()
    collectionView.layoutIfNeeded()

    collectionView.scrollToItemAtIndexPath(
        NSIndexPath(forItem: 0, inSection: startIndex),
        atScrollPosition: .None,
        animated: false)
 }

Le problème semble être dans la mauvaise taille collectionView. Après avoir défini la disposition, scrollToItemAtIndexPath produit le résultat souhaité.

Il semble également que ce problème ne persiste que lorsqu'un Collection View est utilisé dans une UIViewController.

15
victor.vasilica

Swift 3.0 testé et fonctionne.

var onceOnly = false
    internal func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if !onceOnly {
            //set the row and section you need.
            let indexToScrollTo = IndexPath(row: 1, section: indexPath.section)
            self.fotmCollectionView.scrollToItem(at: indexToScrollTo, at: .left, animated: false)
            onceOnly = true
        }
    }
5
kemicofa

Malheureusement, chacune de ces réponses existantes est au moins partiellement erronée ou ne répond pas à la question exacte posée. J'ai travaillé sur cette question avec un collègue qui n'a pas été aidé par aucune de ces réponses. 

Tout ce que vous avez à faire est de définir le décalage de contenu sans animation sur le décalage de contenu correct, puis d'appeler des données de rechargement. (Ou ignorez l'appel reloadData s'il n'a pas encore été chargé.) Vous devez le faire dans viewDidLoad si vous ne voulez jamais que la première cellule soit créée. 

Cette réponse suppose que la vue de collection défile horizontalement et que la taille des cellules a la même taille que la vue, mais le concept est identique si vous souhaitez faire défiler verticalement ou si les cellules ont une taille différente. De plus, si votre CollectionView comporte plusieurs sections, vous devez faire un peu plus de calculs pour calculer le décalage de contenu, mais le concept reste le même.

func viewDidLoad() {
    super.viewDidLoad()
    let pageSize = self.view.bounds.size
    let contentOffset = CGPoint(x: pageSize.width * self.items.count, y: 0)
    self.collectionView.setContentOffset(contentOffset, animated: false)
}
3
Logan Shire

cela semblait fonctionner pour moi:

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

Passez le chemin indexPath du premier VC à la vue de collection de la méthode DidSelectItemAtIndexPath. Dans viewDidLoad de votre vue de collection, utilisez la méthode scrollToItemAtIndexPath:atScrollPosition:animated: Définissez animated sur NO et atScrollPosition sur UICollectionViewScrollPositionNone. Comme ça: 

[self.collectionView scrollToItemAtIndexPath:self.indexPathFromVC atScrollPosition:UICollectionViewScrollPositionNone animated:NO];
2
Stephen Paul

Voici ce qui a fonctionné pour moi (dans une classe UICollectionViewController):

private var didLayoutFlag: Bool = false

public override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if let collectionView = self.collectionView {
        if !self.didLayoutFlag {
            collectionView.scrollToItemAtIndexPath(self.viewModel.initialIndexPath, atScrollPosition: .None, animated: false)
            self.didLayoutFlag = true
        }
    }
}
1
Nikolay Spassov

Une solution plus simple inspirée par d'autres:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    DispatchQueue.main.async {
        self.collectionView.scrollToItem(at: lastIndexPath, section: 0), at: .centeredHorizontally, animated: false)
    }
}

Cela fonctionnera si vous mettez le code dans le bloc DispatchQueue.main.async.

0
Andres Wang

Hey, j'ai résolu avec Objective C ..__ Je pense que cela est utile pour vous . Vous pouvez convertir Objective C en Swift également . Voici mon code:

** Dans mon cas, cliquez sur le bouton J'active l'index spécifique, soit 3 **

pour Vertical

 - (IBAction)Click:(id *)sender {
    NSInteger index=3;
    CGFloat pageHeight = self.collectionView.frame.size.height;
    CGPoint scrollTo = CGPointMake(0, pageHeight * index);
    [self.collectionView setContentOffset:scrollTo animated:YES];
}

Pour l'horizontal

 - (IBAction)Click:(id *)sender {
    NSInteger index=3;
    CGFloat pageWidth = self.collectionView.frame.size.width;
    CGPoint scrollTo = CGPointMake(pageWidth * index, 0);
    [self.collectionView setContentOffset:scrollTo animated:YES];
}

J'espère que cela peut vous aider.

0
user2856484

Je suis arrivé ici avec le même problème et j'ai constaté que, dans mon cas, ce problème était dû au fait que mon ViewController dans mon storyboard était défini en tant que taille «freeform». 

Je suppose que viewWillLayoutSubviews est appelé pour calculer la taille correcte lorsque la vue est chargée pour la première fois, si les dimensions du storyboard laissent à ce que cela ne soit pas clair. (J'avais dimensionné mon viewController dans mon storyboard pour qu'il soit 'libre' afin que je puisse le faire très haut pour voir/éditer de nombreuses cellules dans une longue tableView dans ma collectionView). 

J'ai constaté que l'approche de victor.vasilica et de la serre concernant l'insertion de la commande 'scrollToRow' dans viewWillLayoutSubviews fonctionnait parfaitement pour résoudre le problème. 

Cependant, j’ai également constaté qu’une fois que j’ai redéfini la taille VC de mon storyboard, le problème a immédiatement disparu et que j’ai pu définir la cellule initiale dans viewWillAppear. Votre situation est peut-être différente, mais cela m'a aidé à comprendre ce qui se passait dans ma situation et j'espère que ma réponse pourra aider à informer les autres de ce problème.

0
Natalia