UIScrollView. ¿Alguna idea sobre la implementación de desplazamiento / zoom “infinito”?

Por lo tanto, UITableView es compatible esencialmente con el desplazamiento “infinito”. Puede haber un límite, pero ese imbécil puede desplazarse por un tiempo looonnnggg. Me gustaría imitar este comportamiento con UIScrollView, pero hay dos impedimentos fundamentales:

1) scrollView.contentSize se fija en el momento de la creación. 2) el acercamiento puede hacer volar cualquier esquema de carga lenta al infierno, ya que puede causar una explosión de datos.

¿Han pensado otros en esta idea? Yah, lo sé, básicamente estamos hablando de volver a crear Google Maps aquí. Cualquier idea sería muy apreciada.

Saludos, Doug

Si bien es imposible tener un UIScrollView verdaderamente infinito, hay algunos trucos simples que puedes usar para emular ese comportamiento.

  1. Manejo del tamaño de contentSize fijo: tenga una vista de tamaño fijo manejada por su vista de desplazamiento, y en el inicio o instanciación, establezca el desplazamiento de contenido para que esté viendo el centro de la vista manipulada. Luego, solo mire el offset del contenido (usando KVO o algún otro método), y si está cerca de cualquier borde, actualice el contenido de la vista con un nuevo conjunto de contenido (offset adecuadamente) y restablezca la propiedad contentOffset la vista de contentOffset para volver al medio.
  2. Manejo del zoom: haga algo similar, solo que esta vez observe el factor de zoom en la vista de desplazamiento. Siempre que llegue a cierto punto, realice alguna manipulación con los datos que esté presentando para que aparezca ampliada, luego reinicie el factor de zoom a 1.0. Por ejemplo, si está desplazando una imagen y obtiene el zoom para que aparezca el doble de grande, aplique mediante progtwigción algún tipo de transformación para que la imagen sea dos veces más grande, luego restablezca el factor de zoom de la vista de desplazamiento en 1.0. La imagen seguirá apareciendo ampliada, pero la vista de desplazamiento podrá continuar acercándose más si es necesario. (Google Maps lleva esto un paso más allá donde carga de forma perezosa vistas más detalladas a medida que el usuario amplía; puede optar por implementar esto o no).

Acabo de terminar de implementar el rollo de infitine para mí. En mi implementación tengo UITableViewCell con un scrollView y botones de navegación. El scrollView contiene x vistas todas con el mismo ancho. las vistas se alinean horizontalmente y la paginación está habilitada.

 scrollView.clipsToBounds = YES; scrollView.scrollEnabled = YES; scrollView.pagingEnabled = YES; scrollView.showsHorizontalScrollIndicator = NO; 

Mi código lógico es como el siguiente:

  1. En mi función de inicialización I
    • crear todas las vistas (para la vista de desplazamiento) y
    • ponerlos en una matriz y
    • agrégalos al scrollView
  2. Luego invoco una función que calcula en un bucle las posiciones de cada vista (cada vez que detecte un desplazamiento, también deberá llamarse a esta función). Siempre toma el primer elemento de la matriz y establece el marco en (0,0, …, …), el segundo con (i * ancho, 0, …., ….) y así en. La función llamada es así:

     - (void)updateOffsetsOfViews{ int xpos = 0; for (int i=0; i< [views count]; i++) { UIImageView *_view = [views objectAtIndex:i]; CGRect aFrame = _view.frame; aFrame.origin.x = xpos; aFrame.origin.y = 0.0; _view.frame = aFrame; xpos += viewWidth; } float center = 0; if(fmod([views count],2) == 1){ center = viewWidth * ([views count]-1)/2; }else { center = viewWidth * [views count]/2; } [scrollView setContentOffset:CGPointMake(center, 0)]; lastOffset = center; } 
  3. Luego (aún en el proceso de inicialización) agrego un observador

     [scrollView addObserver:self forKeyPath:@"contentOffset" options:0 context:nil]; 

    así que cada vez que algo en scrollView cambia, obtengo la función (observeValueForKeyPath), que se ve así:

     - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { UIImageView *_viewFirst = (UIImageView *)[views objectAtIndex:0]; if ( fmod([scrollView contentOffset].x,viewWidth) == 0.0) { if ([scrollView contentOffset].x > lastOffset) { [views removeObjectAtIndex:0]; [views addObject:_viewFirst]; [self updateOffsetsOfViews]; }else if ([scrollView contentOffset].x < lastOffset) { UIImageView *_viewLast = (UIImageView *)[views lastObject]; [views removeLastObject]; [views insertObject:_viewLast atIndex:0]; [self updateOffsetsOfViews]; } } } 
  4. Y en dealloc o viewDidUnload (depende de cómo lo implemente), no se olvide de eliminar el observador.

     [scrollView removeObserver:self forKeyPath:@"contentOffset"]; 

Espero que esto ayude, es posible que notes cierta sobrecarga, pero en mi implementación también soporto desplazar 5 páginas (bueno ... ilimitadas) a la vez y autoanimar el desplazamiento, etc. para que puedas ver algo que podría descartarse.

El proyecto de ejemplo StreetScroller de Apple muestra cómo realizar un desplazamiento infinito en UIScrollView.

Tenga en cuenta que cuando el desplazamiento está animado, contentOffset cambia muchas veces, no solo página por página, sino con cada paso de la animación.

Quizás establecer contentSize en un valor gigantesco y luego mover un número limitado de vistas subyacentes para seguir la posición de vista como en la muestra de mosaico hará el truco.

Para mitigar la posibilidad de eventualmente llegar a un borde y tener que volver a centrar abruptamente la vista (que cancela cualquier desplazamiento actualmente en movimiento), la vista puede volverse cuando está estacionario, de vez en cuando.

De todos modos, eso es lo que voy a intentar.