web-dev-qa-db-fra.com

UICollectionVIew: animer les cellules pendant leur défilement

J'aimerais que les éléments de mon UICollectionView s'animent pour être affichés lorsque l'utilisateur fait défiler la liste (j'utilise une sous-classe de UICollectionViewFlowLayout).

Je spécifie la position dans le Layout Manager, ce que j'aimerais pouvoir faire, c'est aussi spécifier une transformation initiale et la faire appliquer dans une animation au bon moment (lorsque la cellule apparaît pour la première fois à l'écran). Pour voir l'effet que je veux dire, consultez l'application Google Plus sur iOS. Idéalement, une transformation différente en fonction de l'emplacement de la cellule.

Je n'arrive pas à trouver un moyen de savoir quand une cellule est affichée (pas d'équivalent de willDisplayCell comme il y en a sur UITableView) ou des pointeurs sur où aller pour cela.

Aucune suggestion?

Vous pouvez à peu près distinguer l'animation dans Google Plus dans cette capture d'écran: Example in the Google Plus App

Jetez également un œil à iPhoto sur l'iPad. Je ne sais pas s'ils utilisent une UIcollectionView (probablement pas, car cela fonctionnait sur iOS5) mais c'est le genre d'effet que je recherche, les photos semblent voler de la droite.

21
Marc

Vous pouvez obtenir cet effet indépendamment d'avoir une mise en page personnalisée.

Le code d'animation doit aller à l'intérieur de collectionView:cellForItemAtIndexPath:

Lorsqu'une cellule est retirée de la file d'attente, son frame sera défini sur ce qui est spécifié dans votre disposition. Vous pouvez le stocker dans une variable temporaire en tant que frame final pour la cellule. Vous pouvez ensuite définir le cell.frame jusqu'à une position initiale en dehors de l'écran, puis animez vers la position finale souhaitée. Quelque chose dans le sens de:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    UICollectionView *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"Cell" forIndexPath:indexPath];

    CGRect finalCellFrame = cell.frame;
    //check the scrolling direction to verify from which side of the screen the cell should come.
    CGPoint translation = [collectionView.panGestureRecognizer translationInView:collectionView.superview];
    if (translation.x > 0) {
        cell.frame = CGRectMake(finalCellFrame.Origin.x - 1000, - 500.0f, 0, 0);
    } else {
        cell.frame = CGRectMake(finalCellFrame.Origin.x + 1000, - 500.0f, 0, 0);
    }

    [UIView animateWithDuration:0.5f animations:^(void){
        cell.frame = finalCellFrame;
    }];

    return cell;
}

Le code ci-dessus animera les cellules sur un UICollectionView horizontal, provenant du haut de l'écran et de chaque côté, selon la direction de défilement. Vous pouvez également vérifier le indexPath actuel pour avoir des cellules animées à partir de différentes positions sur des dispositions plus compliquées, ainsi que pour animer d'autres propriétés avec le cadre, ou appliquer une transformation.

36
Cezar

Vous devez remplacer initialLayoutAttributesForAppearingItemAtIndexPath: dans la présentation de la vue de votre collection. Ici, vous pouvez définir n'importe quel attribut sur l'élément d'attributs de mise en page (y compris la transformation) qui sera animé avec les attributs réels après l'apparition de la cellule.

Si l'objet d'attributs de présentation standard ne vous donne pas suffisamment d'options, vous pouvez le sous-classer, ajouter des attributs supplémentaires et remplacer applyLayoutAttributes: sur votre cellule pour récupérer les propriétés supplémentaires.

Cela ne fonctionnera que lorsque vous ajouterez des cellules à la vue de collection.

Pour appliquer des transformations aux cellules lorsque vous faites défiler la vue de la collection, je voudrais les appliquer directement à la cellule (à cellForItem...) ou dans les attributs de disposition, peut-être une propriété appelée initialTransform. Lorsque vous appliquez les attributs de disposition à la cellule, vous devez vérifier le initialTransform, l'appliquer, puis le définir sur l'identité, puis déclencher une animation pour accéder à la transformation d'identité, de sorte qu'il ne serait appliqué que lorsque la cellule a d'abord défilé sur l'écran. Je n'ai rien mis en œuvre comme ça, c'est juste une supposition sur la façon dont je le ferais.

5
jrturton

Comme Marc l'a mentionné dans un commentaire, qu'il a fini par utiliser une vue de défilement, je veux montrer comment le faire avec une vue de table standard. Ce n'est pas le même effet que dans google + mais pourrait être adapté facilement.

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    static CGFloat duration = .5;
    static NSUInteger xOffset = 50;
    static NSUInteger yOffset = 200;
    if(scrollDirection == STAScrollDirectionDown && lastAnimatedIndex < indexPath.row)
    {
        cell.frame = CGRectMake(cell.frame.Origin.x - xOffset, cell.frame.Origin.y+yOffset, cell.frame.size.width, cell.frame.size.height);
        [UIView animateWithDuration:duration
                         animations:^{
            cell.frame = CGRectMake(cell.frame.Origin.x + xOffset, cell.frame.Origin.y - yOffset, cell.frame.size.width, cell.frame.size.height);
        } completion:^(BOOL finished) {
            lastAnimatedIndex = indexPath.row;
        }];
    }
}

Juste avant d'afficher la cellule, nous attribuons un décalage (et éventuellement une rotation) à chaque cellule et nous la réanimons aux paramètres précédents.


un exemple un peu plus excitant:

-(void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
    CATransform3D rotation = CATransform3DMakeRotation( (90.0*M_PI)/180, .0, 0.5, 0.5);
    cell.contentView.alpha = 0.8;
    cell.contentView.layer.transform = rotation;
    cell.contentView.layer.anchorPoint = CGPointMake(0, 0.5);

    [UIView animateWithDuration:.5
                     animations:^{
                         cell.contentView.layer.transform = CATransform3DIdentity;
                         cell.contentView.alpha = 1;
                         cell.contentView.layer.shadowOffset = CGSizeMake(0, 0);
                     } completion:^(BOOL finished) {
                     }];
}
3
vikingosegundo