2009-06-14 21 views
119

UIScrollViewDelegate tiene dos métodos de delegado scrollViewDidScroll: y scrollViewDidEndScrollingAnimation:, pero ninguno de estos indica cuándo se ha completado el desplazamiento. scrollViewDidScroll solo le notifica que la vista de desplazamiento no se desplazó, no porque ha terminado de desplazarse.Cómo detectar cuando un UIScrollView ha terminado de desplazarse

El otro método scrollViewDidEndScrollingAnimation solo parece activarse si desplaza la vista de desplazamiento por programación no si el usuario se desplaza.

¿Alguien sabe de esquema para detectar cuándo una vista de desplazamiento ha completado el desplazamiento?

+0

Véase también, si se desea detectar movimiento en sentido vertical acabada después de desplazarse mediante programación: http://stackoverflow.com/questions/2358046/is-it-possible-to-be-notified-when-a-uitableview- acabado-desplazamiento – Trevor

Respuesta

127

Los métodos que está buscando son scrollViewDidEndDragging:willDecelerate: y scrollViewDidEndDecelerating:. El primero siempre se llama después de que el usuario levanta su dedo. Si se desplazaron lo suficientemente rápido como para provocar desaceleración, willDecelerate será YES y se llamará al segundo método una vez que se complete la desaceleración.

(From the UIScrollViewDelegate docs.)

+10

Parece que eso solo funciona si desacelera. Si se desplaza lentamente, luego suelta, no llama a didEndDecelerating. –

+3

Aunque es la API oficial. En realidad, no siempre funciona como esperamos. @Ashley Smart dio una solución más práctica. –

+0

Edité la respuesta para aclarar este caso, Sean. –

21

creo scrollViewDidEndDecelerating es el que usted desea. Sus UIScrollViewDelegates método opcional:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 

Le dice el delegado que la vista de desplazamiento ha terminado desacelerar el movimiento de desplazamiento.

UIScrollViewDelegate documentation

+0

Er, di la misma respuesta. :) –

+0

Doh. Un nombre críptico, pero es exactamente lo que estaba buscando. Debería haber leído la documentación mejor. Ha sido un día largo ... –

+0

Esta es la respuesta correcta. – Hahnemann

155

El 320 implementations son mucho mejor - aquí es un parche para obtener start/extremos consistentes del desplazamiento.

-(void)scrollViewDidScroll:(UIScrollView *)sender 
{ 
[NSObject cancelPreviousPerformRequestsWithTarget:self]; 
    //ensure that the end of scroll is fired. 
    [self performSelector:@selector(scrollViewDidEndScrollingAnimation:) withObject:sender afterDelay:0.3]; 

... 
} 

-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 
{ 
    [NSObject cancelPreviousPerformRequestsWithTarget:self]; 
... 
} 
+0

resolvió mi problema de drag - vs scroll end. ¡Gracias! –

+0

La única respuesta útil en SO para mi problema de desplazamiento. ¡Gracias! –

+0

¡Gracias, esto parece funcionar bastante bien para mis necesidades! –

3

He intentado con la respuesta de Ashley Smart y funcionó a las mil maravillas. Aquí hay otra idea, con el uso de solamente scrollViewDidScroll

-(void)scrollViewDidScroll:(UIScrollView *)sender 
{ 
    if(self.scrollView_Result.contentOffset.x == self.scrollView_Result.frame.size.width)  { 
    // You have reached page 1 
    } 
} 

sólo tenía dos páginas por lo que funcionó para mí. Sin embargo, si tiene más de una página, podría ser problemático (podría verificar si el desplazamiento actual es un múltiplo del ancho, pero entonces no sabría si el usuario se detuvo en la segunda página o está en camino hacia el tercero o más)

7

Sólo acabo de encontrar esta pregunta, que es casi lo mismo le pregunté: How to know exactly when a UIScrollView's scrolling has stopped?

Aunque didEndDecelerating funciona cuando el desplazamiento, la panorámica con la liberación estacionaria no queda registrado.

Finalmente encontré una solución. didEndDragging tiene un parámetro WillDecelerate, que es falso en la situación de liberación estacionaria.

Al marcar para! Desacelerar en DidEndDragging, combinado con didEndDecelerating, se obtienen las dos situaciones que se acaban de desplazar.

18

Para todos los rollos relacionados con las interacciones de arrastre, esto será suficiente:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { 
    _isScrolling = NO; 
} 

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 
    if (!decelerate) { 
     _isScrolling = NO; 
    } 
} 

Ahora, si su desplazamiento se debe a una setContentOffset programática/scrollRectVisible (con animated = SÍ o que, obviamente, saber cuándo se terminó de desplazamiento):

- (void)scrollViewDidEndScrollingAnimation { 
    _isScrolling = NO; 
} 

Si el desplazamiento se debe a otra cosa (como la apertura o el cierre del teclado teclado), parece que vas a tener que detectar el evento con un corte debido scrollViewDidEndScrollingAnimation no es útil tampoco.

el caso de un desplazamiento paginado vista:

Debido a que, supongo, Apple aplica una curva de aceleración, scrollViewDidEndDecelerating ser llamado para cada arrastre de modo que no hay necesidad de usar scrollViewDidEndDragging en este caso.

1

Recapitulando (y para novatos). No es tan doloroso Simplemente agregue el protocolo y luego agregue las funciones que necesita para la detección.

En la vista (clase) que contiene el UIScrolView, agregue el protocolo y luego agregue todas las funciones de aquí a su vista (clase).

// -------------------------------- 
// In the "h" file: 
// -------------------------------- 
@interface myViewClass : UIViewController <UIScrollViewDelegate> // <-- Adding the protocol here 

// Scroll view 
@property (nonatomic, retain) UIScrollView *myScrollView; 
@property (nonatomic, assign) BOOL isScrolling; 

// Protocol functions 
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView 
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView; 
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView; 


// -------------------------------- 
// In the "m" file: 
// -------------------------------- 
@implementation BlockerViewController 

- (void)viewDidLoad { 
    CGRect scrollRect = self.view.frame; // Same size as this view 
    self.myScrollView = [[UIScrollView alloc] initWithFrame:scrollRect]; 
    self.myScrollView.delegate = self; 
    self.myScrollView.contentSize = CGSizeMake(scrollRect.size.width, scrollRect.size.height); 
    self.myScrollView.contentInset = UIEdgeInsetsMake(0.0,22.0,0.0,22.0); 
    // Allow dragging button to display outside the boundaries 
    self.myScrollView.clipsToBounds = NO; 
    // Prevent buttons from activating scroller: 
    self.myScrollView.canCancelContentTouches = NO; 
    self.myScrollView.delaysContentTouches = NO; 
    [self.myScrollView setBackgroundColor:[UIColor darkGrayColor]]; 
    [self.view addSubview:self.myScrollView]; 

    // Add stuff to scrollview 
    UIImage *myImage = [UIImage imageNamed:@"foo.png"]; 
    [self.myScrollView addSubview:myImage]; 
} 

// Protocol functions 
- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { 
    NSLog(@"start drag"); 
    _isScrolling = YES; 
} 

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { 
    NSLog(@"end decel"); 
    _isScrolling = NO; 
} 

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { 
    NSLog(@"end dragging"); 
    if (!decelerate) { 
     _isScrolling = NO; 
    } 
} 

// All of the available functions are here: 
// https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIScrollViewDelegate_Protocol/Reference/UIScrollViewDelegate.html 
16

Esto ha sido descrito en algunas de las otras respuestas, pero aquí (en código) cómo combinar scrollViewDidEndDecelerating y scrollViewDidEndDragging:willDecelerate para realizar alguna operación cuando el desplazamiento ha terminado:

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{ 
    [self stoppedScrolling]; 
} 

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView 
        willDecelerate:(BOOL)decelerate 
{ 
    if (!decelerate) { 
     [self stoppedScrolling]; 
    } 
} 

- (void)stoppedScrolling 
{ 
    // done, do whatever 
} 
1

que tenía un caso de tocar y arrastrar acciones y descubrí que el arrastre llamaba a scrollViewDidEndDecelerating

Y el cambio se compensa manualmente con el código ([_scrollView setContentOffset: contentOffset animated: YES];) estaba llamando a scrollView DidEndScrollingAnimation.

//This delegate method is called when the dragging scrolling happens, but no when the  tapping 
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{ 
    //do whatever you want to happen when the scroll is done 
} 

//This delegate method is called when the tapping scrolling happens, but no when the dragging 
-(void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 
{ 
    //do whatever you want to happen when the scroll is done 
} 
2

versión Swift de respuesta aceptada:

func scrollViewDidScroll(scrollView: UIScrollView) { 
    // example code 
} 
func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { 
     // example code 
} 
func scrollViewDidEndZooming(scrollView: UIScrollView, withView view: UIView!, atScale scale: CGFloat) { 
     // example code 
} 
0

UIScrollView tiene un método delegado

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 

Añadir las siguientes líneas de código en el método delegado

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView 
{ 
CGSize scrollview_content=scrollView.contentSize; 
CGPoint scrollview_offset=scrollView.contentOffset; 
CGFloat size=scrollview_content.width; 
CGFloat x=scrollview_offset.x; 
if ((size-self.view.frame.size.width)==x) { 
    //You have reached last page 
} 
} 
1

Si alguien necesita, aquí está Ashley Smart responder en Swift

func scrollViewDidScroll(_ scrollView: UIScrollView) { 
     NSObject.cancelPreviousPerformRequests(withTarget: self) 
     perform(#selector(UIScrollViewDelegate.scrollViewDidEndScrollingAnimation), with: nil, afterDelay: 0.3) 
    ... 
} 

func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) { 
     NSObject.cancelPreviousPerformRequests(withTarget: self) 
    ... 
} 
2

solución justa desarrollado para detectar cuando se desplaza acabada aplicación en todo: https://gist.github.com/k06a/731654e3168277fb1fd0e64abc7d899e

Se basa en la idea de seguimiento de los cambios de los modos Runloop. Y realice bloques al menos después de 0.2 segundos después de desplazarse.

Esta es la idea central para el seguimiento de los modos Runloop cambia iOS10 +:

- (void)tick { 
    [[NSRunLoop mainRunLoop] performInModes:@[ UITrackingRunLoopMode ] block:^{ 
     [self tock]; 
    }]; 
} 

- (void)tock { 
    self.runLoopModeWasUITrackingAgain = YES; 
    [[NSRunLoop mainRunLoop] performInModes:@[ NSDefaultRunLoopMode ] block:^{ 
     [self tick]; 
    }]; 
} 

y la solución para los destinos de despliegue bajas como iOS2 +:

- (void)tick { 
    [[NSRunLoop mainRunLoop] performSelector:@selector(tock) target:self argument:nil order:0 modes:@[ UITrackingRunLoopMode ]]; 
} 

- (void)tock { 
    self.runLoopModeWasUITrackingAgain = YES; 
    [[NSRunLoop mainRunLoop] performSelector:@selector(tick) target:self argument:nil order:0 modes:@[ NSDefaultRunLoopMode ]]; 
} 
0

Hay un método de UIScrollViewDelegate que se puede utilizar para detectar (o mejor dicho, 'predecir') cuando el desplazamiento realmente ha terminado:

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) 

de UIScrollViewDelegate que se puede usar para detectar (o mejor dicho, 'predecir') cuando el desplazamiento realmente ha terminado.

En mi caso he utilizado con el desplazamiento horizontal de la siguiente manera (en Swift 3):

func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) { 
    perform(#selector(self.actionOnFinishedScrolling), with: nil, afterDelay: Double(velocity.x)) 
} 
func actionOnFinishedScrolling() { 
    print("scrolling is finished") 
    // do what you need 
} 
1

Una alternativa sería utilizar scrollViewWillEndDragging:withVelocity:targetContentOffset que se llama cuando el usuario levanta el dedo y contiene el destino desplazamiento de contenido donde se detendrá el desplazamiento. El uso de este desplazamiento de contenido en scrollViewDidScroll: identifica correctamente cuándo la vista de desplazamiento ha dejado de desplazarse.

private var targetY: CGFloat? 
public func scrollViewWillEndDragging(_ scrollView: UIScrollView, 
             withVelocity velocity: CGPoint, 
             targetContentOffset: UnsafeMutablePointer<CGPoint>) { 
     targetY = targetContentOffset.pointee.y 
} 
public func scrollViewDidScroll(_ scrollView: UIScrollView) { 
    if (scrollView.contentOffset.y == targetY) { 
     print("finished scrolling") 
    } 
Cuestiones relacionadas