inside example doesn iphone ios uiscrollview core-animation catiledlayer

iphone - example - swift uiscrollview autolayout



Desplazamiento no deseado al animar zoomScale en UIScrollView (5)

Creo que tiene que manipular el contentInset y / o contentOffset usted mismo, en cada evento de desplazamiento. Manipular los puntos de anclaje no ayudará. Mira esta pregunta y mi respuesta.

Resumen ejecutivo: en ocasiones, UIScrollView realiza un cambio no deseado en el valor de contentOffset , lo que hace que la aplicación muestre la ubicación incorrecta en el documento que se está viendo. El cambio no deseado ocurre junto con un cambio animado en zoomScale la vista de zoomScale .

Los detalles: tengo problemas al alejar con CATiledLayer en UIScrollView . El CATiledLayer contiene un pdf, y cuando contentOffset está dentro de un cierto rango, cuando contentOffset , el contentOffset se cambia (ese es el error) antes de que se produzca el zoom. El contentOffset parece haber cambiado en el código de Apple.

Para ilustrar el problema, modifiqué la aplicación de muestra de Apple, ZoomingPDFViewer. El código está en github: https://github.com/DirkMaas/ZoomingPDFViewer-bug

Un toque hará que zoomScale se cambie a 0.5, usando animateWithDuration , y así se animateWithDuration . Si el contentOffset.y UIScrollView es inferior a aproximadamente 2700 o superior a 5900, la animación de zoomScale funciona bien. Si el tap ocurre cuando contentOffset.y está entre esos dos valores, contentOffset.y saltará (no animado) a aproximadamente 2700, y luego se producirá la animación de zoomScale , pero el desplazamiento ocurrirá al mismo tiempo, de modo que cuando la animación está hecho, contentOffset.y está donde debería estar. Pero, ¿de dónde viene el salto?

Por ejemplo, supongamos que contentOffset.y es 2000 cuando se contentOffset.y la pantalla: la animación de zoomScale funciona perfectamente; contentOffset.y no se cambia.

Pero si contentOffset.y es 4000 cuando se contentOffset.y la pantalla: contentOffset.y saltará, sin animación, a aproximadamente 2700, y luego el zoom y el desplazamiento comenzarán desde ese punto y se producirán al mismo tiempo. Cuando termina la animación, parece que hicimos un zoom desde 4000, así que terminamos en el lugar correcto, pero el comportamiento es incorrecto.

Una nota sobre la IU:

  • el texto se puede desplazar verticalmente de la manera normal
  • el texto puede acercarse y alejarse pellizcando de la manera normal
  • un solo toque hará que el zoomScale se configure en 0.5; el cambio está animado

Me he dado cuenta de que si zoomScale es mayor que 0.5, el salto no es tan grande. Además, si uso setZoomScale:animated: lugar de animateWithDuration , el error desaparece, pero no puedo usarlo porque necesito encadenar animaciones.

Aquí hay un resumen de lo que hice (el código en github incluye estos cambios):

  • Se descargó ZoomingPDFViewer de http://developer.apple.com/library/ios/#samplecode/ZoomingPDFViewer/Introduction/Intro.html y se abrió en XCode.
  • Cambió la configuración de compilación | Arquitecturas | Base SDK a iOS más reciente (iOS 4.3) cambiado Configuraciones de compilación | GCC 4.2 - Idioma | Compilar las fuentes en cuanto a Objective-C ++
  • eliminado TestPage.pdf del proyecto
  • se agregó "whoiam 5 24 cropped 3-2.pdf" al proyecto en su lugar
  • se agregó PDFScrollView *scrollView; a la clase ZoomingPDFViewerViewController
  • se ha cambiado loadView en ZoomingPDFViewerViewController para inicializar scrollView lugar de sv
  • agregado viewDidLoad , handleTapFrom:recognizer y zoomOut a ZoomingPDFViewerViewController en PDFScrollview.m
  • comentó scrollViewDidEndZooming:withView:atScale y scrollViewWillBeginZooming:withView: porque hacen cosas en el fondo de la imagen que distraen del problema en cuestión

¡Muchas gracias por acompañarme, y toda ayuda!



He estado jugando con el código que publicaste y creo que he encontrado lo que está pasando. Cuando haces una animación en bloque como en tu código:

[UIView animateWithDuration:4.0 animations:^ { self.scrollView.zoomScale = 0.5; } completion:nil];

El bloque de animación en realidad se llama al comienzo de la animación. Core Animation maneja internamente las capas que hacen que parezca que realmente se está moviendo. Hay una lista de Propiedades Animables en el Dev Center, y zoomScale no está entre ellas.

Cuando el zoomScale cambia, el scrollView actualiza automáticamente su contentOffset. Entonces, cuando comienza la animación, el salto que está viendo es el contentOffset que se está ajustando para una nueva escala de zoom. Luego, la capa se anima con la escala de zoom adecuada, pero el objetivo contentOffset (es decir, qué contenido debe tener contentOffset al final del zoom) ya está establecido al comienzo del zoom.

Esa también es la razón por la cual el zoom parece estar centrado alrededor de un punto fuera de la pantalla. Tendrás que usar setZoomScale: animated: o bien puedes animar la capa y el offset tú mismo ...


Tuve muchos errores con el zoom cuando mi función viewForZoomingInScrollView devolvía la vista de desplazamiento directamente.

Muchos de esos comportamientos extraños desaparecieron después de establecer una vista dentro de scrollView que contenía todo y devolverlo en viewForZoomingInScrollView. Quizás esto debería resolver tu problema también:

-(void) someIntializationFunction { [myScrollView addSubView:self.globalContainer]; // Now had everything in the container and not in the scrollView directly .... } - (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView; { return self.globalContainer; }

Otra cosa, he visto zoomToRect: función mucho más confiable que cambiar el zoomScale de la scrollView. Esto podría ayudar también, incluso si tendrá más cálculos para hacer ...


Una de las cosas más complicadas de entender sobre el zoom es que siempre ocurre alrededor de un punto llamado Anchor Point. Creo que la mejor manera de entenderlo es imaginar un sistema de coordenadas superpuesto a otro. Diga A es su sistema de coordenadas externo, B es el interno (B será la vista de desplazamiento). Cuando el desplazamiento de B es (0,0) y la escala es 1,0, entonces el punto B (0,0) corresponde a A (0,0), y en general B (x, y) = A (x, y )

Además, si el desplazamiento de B es (xOff, yOff) entonces B (x, y) = A (x - xOff, y - yOff). De nuevo, esto sigue suponiendo que la escala de zoom es 1.0.

Ahora, deje que el desplazamiento sea (0,0) nuevamente e imagine lo que sucede cuando intenta hacer zoom. Debe haber un punto en la pantalla que no se mueva al hacer zoom, y cualquier otro punto se mueve hacia afuera desde ese punto. Eso es lo que define el punto de anclaje. Si su anclaje es (0,0), el punto inferior izquierdo permanecerá fijo mientras que todos los demás puntos se mueven hacia arriba y hacia la derecha. En este caso, el desplazamiento sigue siendo el mismo.

Si su punto de anclaje es (0.5, 0.5) (el punto de anclaje está normalizado, es decir, de 0 a 1, entonces 0.5 está a medio camino), entonces el punto central permanece fijo mientras todos los demás puntos se mueven hacia afuera. Esto significa que el desplazamiento debe cambiar para reflejar eso. Si está en un iPhone en modo vertical y se acerca a la escala 2.0, el valor del punto de anclaje x se moverá a la mitad del ancho de la pantalla, 320/2 = 160.

La posición real de la vista de contenido de las vistas de desplazamiento en la pantalla se define tanto por el desplazamiento como por el punto de anclaje. Por lo tanto, si simplemente cambia el punto de anclaje de capas debajo sin hacer un cambio correspondiente en el desplazamiento, verá que la vista parece saltar a una ubicación diferente, aunque el desplazamiento sea el mismo.

Supongo que este es el problema subyacente aquí. Cuando está animando el zoom, Core Animation debe elegir un nuevo punto de anclaje para que el zoom se "vea" a la derecha. Esto también cambiará el desplazamiento para que las vistas de la región visible real en la pantalla no salten. Intente registrar la ubicación del punto de anclaje en varias ocasiones a lo largo de este proceso (se define en el CALayer subyacente de cualquier Vista a la que acceda con una propiedad de "capa" Vistas).

También, por favor mira la documentación aquí para obtener imágenes bonitas y probablemente una mejor descripción de la situación que la que he dado aquí :)

Finalmente, aquí hay un fragmento de código que utilicé en una aplicación para cambiar el punto de anclaje de una capa sin que se mueva en la pantalla.

-(CGPoint)setNewAnchorPointWithoutMoving:(CGPoint)newAnchor { CGPoint currentAnchor = CGPointMake(self.anchorPoint.x * self.contentSize.width, self.anchorPoint.y * self.contentSize.height); CGPoint offset = CGPointMake((1 - self.scale) * (currentAnchor.x - newAnchor.x), (1 - self.scale) * (currentAnchor.y - newAnchor.y)); self.anchorPoint = CGPointMake(newAnchor.x / self.contentSize.width, newAnchor.y / self.contentSize.height); self.position = CGPointMake(self.position.x + offset.x, self.position.y + offset.y); return offset; }