La subvista de scrollview pegajoso tiene un marco incorrecto después de desplazarse rápidamente. Ejemplo comstackble

He pasado las últimas 12 horas jugando con esto y me voy a enfrentar.

Ver en bitbucket: https://bitbucket.org/burneraccount/scrollviewexample

git https: https://bitbucket.org/burneraccount/scrollviewexample.git

git ssh: git@bitbucket.org:burneraccount/scrollviewexample.git

^ Ese es un proyecto Xcode condensado e independiente que ejemplifica el problema que estoy teniendo en mi trabajo real.

Resumen

Estoy tratando de lograr un efecto de image estática, donde la image (una roca en el ejemplo) aparece pegada a la pantalla mientras que el contenido inferior se desplaza hacia arriba y parece que va por encima de la vista de desplazamiento.

Hay un tamaño de pantalla (UIScrollView*)mainScrollView . Este scrollview tiene un contenido (UIView*)contentView . contentView tiene una contentView ~ 1200 ptos y tiene dos subvistas: (UIScrollView *) imageScroller y (UITextView *) textView.

uno

Todo funciona bien hasta que se desplaza un poco hacia arriba, luego se desplaza hacia abajo muy rápido. La position resultante después del movimiento se detiene es incorrecta. De alguna manera, no se actualiza correctamente en el método del delegado scrollviewDidScroll . El desplazamiento rápido hacia abajo (solo después de que se haya arrastrado hacia arriba) se comporta de alguna manera correctamente, pero sigue produciendo una vista fuera de lugar:

dos

arrastrando suavemente no hace casi nada, y la velocidad del desplazamiento está directamente relacionada con el desplazamiento incorrecto.

Aquí está el código infractor:

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView == self.mainScrollView) { CGFloat mainOffset = self.mainScrollView.contentOffset.y; if (mainOffset > 0 && mainOffset < self.initialImageScrollerFrame.size.height) { CGRect imageFrame = self.imageScroller.frame; CGFloat offset = mainOffset-self.lastOffset; self.imageScroller.frame = CGRectMake(imageFrame.origin.x, imageFrame.origin.y + offset, // scroll up imageFrame.size.width, imageFrame.size.height - offset); // hide the bottom of the image self.lastOffset = mainOffset; } } } 

He reestructurado esto en casi todos los sentidos que pude imaginar y siempre tiene efectos similares. La peor parte es cómo el código simple y directo no puede hacer lo que parece que está garantizado. También es extraño que el efecto de zoom que uso en mi proyecto real funciona bien utilizando el mismo mecanismo para dimensionar el mismo elemento de vista. Se ejecuta en el interior (contentOffset < 0) lugar de (contentOffset > 0) , por lo que solo hace zoom en imageScroller cuando tira de la vista debajo de su desplazamiento normal. Eso me lleva a conspirar que se pierden algunos datos a medida que cruza el contenido de contentOffset 0 , pero mis conspiraciones han sido derribadas todo el día.

Este ejemplo es mucho less complicado que el proyecto real en el que estoy trabajando, pero reproduce correctamente el problema. Avíseme si no puede abrir mi proyecto o no puede conectarse al repository.

Es probable que haya una solución bastante obvia para esto, pero estoy horas más allá del punto de tener una nueva perspectiva. Estaré completamente sorprendido si alguna vez esto funciona.

 - (void)scrollViewDidScroll:(UIScrollView *)scrollView { if (scrollView == self.mainScrollView) { CGFloat mainOffset = self.mainScrollView.contentOffset.y; if (mainOffset >= 0 && mainOffset < self.initialImageScrollerFrame.size.height) { CGRect imageFrame = self.imageScroller.frame; CGFloat offset = self.mainScrollView.contentOffset.y-self.lastOffset; if ( imageFrame.origin.y + offset > 0) { //[2] self.imageScroller.frame = CGRectMake(imageFrame.origin.x, imageFrame.origin.y + offset, imageFrame.size.width, imageFrame.size.height - offset); self.lastOffset = mainOffset; } }else if (mainOffset < 0) { //[1] self.imageScroller.frame = self.initialImageScrollerFrame; } } } 

[1]: cuando su mainOffset va por debajo de cero, debe restablecer el marco imageScroller. ScrollViewDidScroll no se registra con el movimiento de cada píxel, solo se llama de vez en cuando (intente iniciar session, verá). Entonces, mientras se desplaza más rápido, sus desplazamientos están más separados. Esto puede dar como resultado un desplazamiento positivo de say +15 convirtiéndose en un desplazamiento negativo en la próxima llamada a scrollViewDiDScroll. Por lo tanto, su imageScroller.frame.y puede quedarse atrapado en ese desplazamiento +15, incluso cuando espera que sea cero. Así, tan pronto como detecta un valor negativo para mainOffset, debe restablecer su marco imageScroller.

[2] – de manera similar, aquí debe asegurarse aquí de que su imageScroller.frame.y siempre será + ve.

Estas correcciones hacen el trabajo, pero todavía las considero "feas y no son dignas de producción". Para un enfoque más limpio, es posible que desee reconsiderar la interacción entre estas vistas y lo que está intentando lograr.

Por cierto, tu 'image.png' es en realidad un jpg. Mientras que estos se ajustan bien al simulador, se romperá (con compiler errors) en un dispositivo.

Publicaré mis actualizaciones aquí.

Se mencionó un respondedor - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView y aunque no se aplica aquí, el scrollViewDidEndDecelerating: similar scrollViewDidEndDecelerating: hace. En mis luchas ayer implementé

 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { if (self.mainScrollView.contentOffset.y < 10) { [UIView animateWithDuration:0.2 animations:^{ self.imageScroller.frame = self.initialImageScrollerFrame; }]; } } 

y esto es de alguna manera una solución, pero es feo y definitivamente no es digno de producción. Simplemente animó el marco a su position original si el contenido de Offset es lo suficientemente cercano a 0. Es un paso en la dirección correcta, pero me temo que no merece el mérito como solución.

—-

También se agregó

 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset { CGPoint point = *targetContentOffset; CGFloat offset = point.y; if (0 <= offset && offset < 10) { [UIView animateWithDuration:0.2 animations:^{ self.imageScroller.frame = self.initialImageScrollerFrame; }]; } } 

lo que me acerca al efecto deseado, pero sigue siendo inutilizable para una aplicación de producción.

Oye, utiliza el siguiente método que podría ayudarte, es un método delegado de UIScrollView:

 - (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView 

Y dentro de este método puede cambiar el marco de desplazamiento como xey a 0,0