Animar UIScrollView contentInset causa tartamudeo de salto


Implementé un control de actualización personalizado (mi propia clase, no una subclase), y por alguna razón desde que me mudé a iOS 8, configurar el contentInset de la vista de desplazamiento (específicamente, UICollectionView) para iniciar la animación de actualización causa un salto/tartamudeo extraño. Aquí está mi código:

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

Básicamente una vez que el usuario libera a refresh, animo el contentInset a la altura del control de refresh. Me imagino que la animación reduciría la tartamudez/nerviosismo, lo que hizo en iOS 7. Pero en iOS 8, cuando la vista de desplazamiento se libera de arrastrar, en lugar de solo animar al contentInset, el contenido de la vista de desplazamiento salta hacia abajo desde el punto de liberación muy rápidamente, y luego se anima suavemente. No estoy seguro si esto es un error en iOS 8 o qué. También he intentado añadir:

scrollView.contentOffset = CGPointZero;

En el bloque de animación, que no cambió nada.

¿alguien tiene alguna idea? Cualquier ayuda sería muy apreciada. ¡Gracias!

Author: ryanthon, 2014-09-26

7 answers

Cambié el método con mi bloque de animación a:

- (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
Author: ryanthon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
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];
            }];
        }
    }
}

Puedes probar esto,la clave es usar dos animaciones.

 5
Author: user3044484,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-08-03 12:59:47

Para eliminar el salto, la respuesta de ryanthon hará el truco. En mi aplicación con iOS9.0, no se necesita ningún bloque de animación para eliminar el salto, solo restablecer contentOffset después de configurar contentInset hará el truco.

Si desea controlar la velocidad de desplazamiento de la vista de tabla al ocultar la vista de actualización, double animation blocks trick en la respuesta de user3044484 hará la magia, un bloque de animación no es suficiente.

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
Author: Zack Zhu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-20 20:16:50

Tuve el mismo problema, y moví mi bloque de animación a la...

- (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView

Delegar el método en su lugar. No es perfecto, pero aún no puedo identificar el problema. Parece que el método -layoutSubviews de ScrollView se llama más a menudo bajo iOS8, causando animaciones nerviosas.

 2
Author: Siôn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-29 16:44:38

Dos soluciones:

  1. desactiva el rebote al iniciar la carga y habilita el rebote al finalizar la carga
  2. después de configurar la inserción de contenido al iniciar la carga, establezca el desplazamiento de contenido con un valor constante
 2
Author: Linkou Bian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-01 10:17:18

He resuelto por:-

  • Deshabilita el Rebote cuando contentInset se actualiza a contentOffsetY .
  • Realice un seguimiento de contentOffset y Actualice a 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)
        }
    }

Github Demo Swift-3

 1
Author: Shrawan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-10 12:17:25

Animar en - (void)scrollViewWillBeginDecelerating:(UIScrollView *)scrollView desafortunadamente no funciona para mí. El scrollview rebota de todos modos.

De acuerdo con Los límites cambian automáticamente en UIScrollView con inserciones de contenido esto me parece un error de iOS8...

 0
Author: niggeulimann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:03:05