web-dev-qa-db-fra.com

L'animation de UIScrollView contentInset provoque un saut d'obstacles

J'ai implémenté un contrôle d'actualisation personnalisé (ma propre classe, pas une sous-classe), et pour une raison quelconque depuis le passage à iOS 8, la définition de contentInset de la vue de défilement (plus précisément, UICollectionView) pour démarrer l'animation d'actualisation provoque un saut/un bégaiement étrange. Voici mon code:

- (void)containingScrollViewDidScroll:(UIScrollView *)scrollView
{
    CGFloat scrollPosition = scrollView.contentOffset.y + scrollView.contentInset.top;

    if( scrollPosition > 0 || self.isRefreshing )
    {
        return;
    }

    CGFloat percentWidth = fabs( scrollPosition ) / self.frame.size.height / 2;

    CGRect maskFrame = self.maskLayer.frame;

    maskFrame.size.width = self.imageLayer.frame.size.width * percentWidth;

    [CATransaction begin];
    [CATransaction setValue:(id)kCFBooleanTrue forKey:kCATransactionDisableActions];
    self.maskLayer.frame = maskFrame;
    [CATransaction commit];
}

- (void)containingScrollViewDidEndDragging:(UIScrollView *)scrollView
{
    if( ( self.maskLayer.frame.size.width >= self.imageLayer.frame.size.width ) && !self.isRefreshing )
    {
        self.isRefreshing = YES;
        [self setLoadingScrollViewInsets:scrollView];
        [self startAnimation];
        [self sendActionsForControlEvents:UIControlEventValueChanged];
    }
}

- (void)setLoadingScrollViewInsets:(UIScrollView *)scrollView
{
    UIEdgeInsets loadingInset = scrollView.contentInset;
    loadingInset.top += self.frame.size.height;

    UIViewAnimationOptions options = UIViewAnimationOptionAllowUserInteraction | UIViewAnimationOptionBeginFromCurrentState;

    [UIView animateWithDuration:0.2 delay:0 options:options animations:^
    {
        scrollView.contentInset = loadingInset;
    }
    completion:nil];
}

En gros, une fois que l'utilisateur relâche pour rafraîchir, j'anime le contentInset à la hauteur du contrôle de rafraîchissement. Je pense que l’animation réduirait le bégaiement/l’agitation, ce qu’elle a fait dans iOS 7. Mais dans iOS 8, lorsque le scrollView est libéré du glissement, au lieu d’animer uniquement le contentInset, le contenu de la vue de défilement descend vraiment du point de sortie rapidement, puis s'anime en douceur. Je ne sais pas s'il s'agit d'un bogue dans iOS 8 ou quoi. J'ai aussi essayé d'ajouter:

scrollView.contentOffset = CGPointZero;

dans le bloc d'animation, qui n'a rien changé.

Quelqu'un a-t-il une idée? Toute aide serait très appréciée. Merci!

28
ryanthon

J'ai changé la méthode avec mon bloc d'animation en: 

- (void)setLoadingScrollViewInsets:(UIScrollView *)scrollView
{
    UIEdgeInsets loadingInset = scrollView.contentInset;
    loadingInset.top += self.view.frame.size.height;

    CGPoint contentOffset = scrollView.contentOffset;

    [UIView animateWithDuration:0.2 animations:^
    {
        scrollView.contentInset = loadingInset;
        scrollView.contentOffset = contentOffset;
    }];
}
30
ryanthon
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    CGPoint point = scrollView.contentOffset;

    static CGFloat refreshViewHeight = 200.0f;

    if (scrollView.contentInset.top == refreshViewHeight) {
        //openning
        if (point.y>-refreshViewHeight) {
            //will close
            //必须套两层animation才能避免闪动!
            [UIView animateWithDuration:0 animations:NULL completion:^(BOOL finished) {
                [UIView animateWithDuration:0.25 animations:^{
                    scrollView.contentOffset = CGPointMake(0.0f, 0.0f);
                    scrollView.contentInset = UIEdgeInsetsMake(0.0f, 0.0f, 0.0f, 0.0f);
                } completion:NULL];
            }];

        }
    }
    else{
        //closing
        static CGFloat openCriticalY = 40.0f;//会执行打开的临界值
        if(point.y<-openCriticalY){
            //will open
            [UIView animateWithDuration:0 animations:NULL completion:^(BOOL finished) {
                [UIView animateWithDuration:0.25 animations:^{
                    scrollView.contentInset = UIEdgeInsetsMake(refreshViewHeight, 0.0f, 0.0f, 0.0f);
                    scrollView.contentOffset = CGPointMake(0.0f, -refreshViewHeight);
                } completion:NULL];
            }];
        }
    }
}

vous pouvez essayer ceci, la clé utilise deux animations.

5
user3044484

Pour supprimer le saut, la réponse de Ryanthon fera l'affaire. Dans mon application avec iOS9.0, aucun bloc d'animation n'est même nécessaire pour supprimer le saut. Réinitialiser contentOffset après avoir défini contentInset fera l'affaire.

Si vous souhaitez contrôler la vitesse de défilement de tableView lorsque vous masquez la vue d'actualisation, l'astuce des blocs d'animation double dans la réponse de user3044484 fera l'affaire, un bloc d'animation ne suffit pas. 

if (self.tableView.contentInset.top == 0)
{
    UIEdgeInsets tableViewInset = self.tableView.contentInset;
    tableViewInset.top = -1.*kTableHeaderHeight;
    [UIView animateWithDuration: 0 animations: ^(void){} completion:^(BOOL finished) {
        [UIView animateWithDuration: 0.5 animations:^{
        //no need to set contentOffset, setting contentInset will change contentOffset accordingly.
            self.tableView.contentInset = tableViewInset;  
        }];
    }];
}
3
Zack Zhu

Deux solutions:

  1. désactiver le rebond lorsque vous commencez à charger et activer le rebond lorsque le chargement est terminé
  2. après avoir défini le contenu au début du chargement, définissez le contenu avec une valeur constante
2
Linkou Bian

J'ai résolu par: -

  • Désactivez le rebond lorsque ContentInset est mis à jour vers contentOffsetY.
  • Gardez une trace de contentOffset et mettez à jour tableView ContentInset.
 var contentOffsetY:CGFloat = 100
    func tracScrollContent(location: UIScrollView) {
        if(location.contentOffset.y  == -contentOffsetY ) {
            tblView.isScrollEnabled = true
            tblView.bounces = false
            tblView.contentInset = UIEdgeInsets(top: contentOffsetY, left: 0, bottom: 0, right: 0)
        }else if(location.contentOffset.y >= 0 ){
            tblView.bounces = true
            tblView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
        }
    }

Démo de Github Swift-3

1
Shrawan

animer dans - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView ne fonctionne malheureusement pas pour moi. Le scrollview rebondit quand même.

Selon Bounds change automatiquement sur UIScrollView avec des insertions de contenu cela me semble être un bug iOS8 ...

0
niggeulimann