Animating UIScrollView contentenset causes jump stutter

j'ai implémenté un contrôle de rafraîchissement personnalisé (ma propre classe, pas une sous-classe), et pour une raison quelconque depuis le passage à iOS 8, paramétrer le contenu de la vue scroll (spécifiquement, UICollectionView) pour démarrer l'animation de rafraîchissement provoque un saut/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];
}

essentiellement une fois que l'utilisateur libère pour rafraîchir, j'anime le contentenset à la hauteur de la commande de rafraîchissement. Je pense que l'animation réduirait le bégaiement / sautillement, qui c'était dans iOS 7. Mais dans iOS 8, quand le scrollView est libéré de glisser, au lieu de simplement animer au contentInset, le contenu de la vue de rouleau saute vers le bas du point de libération vraiment rapidement, et puis s'anime en douceur. Je ne sais pas si c'est un bug 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 une idée? Toute aide serait très appréciée. Merci!

28
demandé sur ryanthon 2014-09-26 05:57:17

7 réponses

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;
    }];
}
27
répondu ryanthon 2015-02-14 10:50:01
- (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
répondu user3044484 2015-08-03 12:59:47

pour enlever 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, il suffit de réinitialiser contentOffset après avoir paramétré contentenset fera l'affaire.

si vous voulez contrôler la vitesse de défilement de tableView en masquant la vue de rafraîchissement, double animation blocks trick dans la réponse de user3044484 fera la magie, un bloc d'animation n'est pas suffisant.

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
répondu Zack Zhu 2016-07-20 20:16:50

j'ai eu le même problème, et déplacé mon bloc d'animation à l'...

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView

méthode de délégation à la place. Pas parfait, mais je ne peux pas encore identifier le problème. On dirait que la méthode -layoutSubviews de scrollView est appelée plus souvent sous iOS8, provoquant des animations sautillantes.

2
répondu Siôn 2014-09-29 16:44:38

deux solutions:

  1. désactiver le rebond au début du chargement et permettre le rebond à la fin du chargement
  2. après avoir défini le contenu inset au début du chargement, veuillez définir le décalage du contenu avec une valeur constante
2
répondu Linkou Bian 2015-09-01 10:17:18

j'ai résolu par: -

  • désactiver le rebond lorsque Contentenset est mis à jour pour contentOffsetY .
  • garder la trace de contentOffset et mettre à jour à tableView Contentenset.
 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)
        }
    }

Github Demo Swift-3

1
répondu Shrawan 2017-01-10 12:17:25

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

selon les limites changent automatiquement sur UIScrollView avec les insets de contenu cela ressemble à un bug iOS8 pour moi...

0
répondu niggeulimann 2017-05-23 12:03:05