iphone - tutorial - ¿Cómo reinicio después de un zoom UIScrollView?
uiscrollview autolayout (6)
Solo estaba buscando restablecer en carga a la escala de zoom predeterminada, porque cuando la acerco, scrollview tiene la misma escala de zoom para la próxima vez hasta que se desasigna.
-(void) viewWillAppear:(BOOL)animated {
[self.scrollView setZoomScale:1.0f];
}
Cambió el juego.
Tengo un gráfico dibujado dentro de UIScrollView
. Es una UIView
grande que usa una subclase personalizada de CATiledLayer
como su capa.
Cuando viewForZoomingInScrollView
y viewForZoomingInScrollView
UIScrollView
, quiero que el gráfico viewForZoomingInScrollView
tamaño dinámicamente, como lo hace cuando devuelvo el gráfico de viewForZoomingInScrollView
. Sin embargo, el Gráfico se redibuja en el nuevo nivel de zoom, y quiero restablecer la escala de transformación a 1x1 para que la próxima vez que el usuario haga zoom, la transformación comience desde la vista actual. Si restablezco la transformación a Identidad en scrollViewDidEndZooming
, funciona en el simulador, pero arroja un EXC_BAD_ACCSES
en el dispositivo.
Esto ni siquiera resuelve el problema por completo en el simulador, porque la próxima vez que el usuario amplía, la transformación se restablece a sí misma al nivel de zoom en el que estaba, y así parece, si tuviera un zoom de 2x, por ejemplo, de repente está en 4x. Cuando termino el zoom, termina en la escala correcta, pero el acto real de acercamiento se ve mal.
Entonces, primero: ¿cómo puedo permitir que el gráfico se vuelva a dibujar en la escala estándar de 1x1 después de hacer zoom, y cómo puedo hacer un zoom uniforme durante todo el proceso?
Editar: Nuevos hallazgos El error parece ser " [CALayer retainCount]: message sent to deallocated instance
"
Nunca voy a desasignar ninguna capa yo mismo. Antes, ni siquiera borraba ningún punto de vista ni nada. Este error fue lanzado sobre el zoom y también sobre la rotación. Si elimino el objeto antes de la rotación y lo vuelvo a agregar después, no arroja la excepción. Esta no es una opción para hacer zoom.
Tengo una discusión detallada de cómo (y por qué) el zoom UIScrollView funciona en github.com/andreyvit/ScrollingMadness/ .
(El enlace también contiene una descripción de cómo ampliar mediante programación el UIScrollView, cómo emular el estilo de biblioteca de fotografías + zoom + desplazamiento, un proyecto de ejemplo y una clase ZoomScrollView que encapsula parte de la magia de acercamiento).
Citar:
UIScrollView no tiene una noción de un "nivel de zoom actual", ya que cada subvista que contiene puede tener su propio nivel de zoom actual. Tenga en cuenta que no hay ningún campo en UIScrollView para mantener el nivel de zoom actual. Sin embargo, sabemos que alguien almacena ese nivel de zoom, porque si pellizcas y alejas una subvista, restableces su transformación a CGAffineTransformIdentity y pellizcas de nuevo, notarás que se ha restaurado el nivel de zoom anterior de la subvista.
De hecho, si observa el desmontaje, es UIView el que almacena su propio nivel de zoom (dentro del objeto UIGestureInfo apuntado por el campo _gestureInfo). También tiene un conjunto de buenos métodos no documentados como zoomScale
y setZoomScale:animated:
(Tenga en cuenta que también tiene un montón de métodos relacionados con la rotación, tal vez recibamos apoyo de gestos de rotación algún día pronto).
Sin embargo, si creamos una nueva UIView solo para hacer zoom y agregamos nuestra vista real con zoom como su elemento secundario, siempre comenzaremos con el nivel de zoom 1.0. Mi implementación del zoom programático se basa en este truco.
Sin embargo, tenga en cuenta que la solución provista por Brad tiene un problema: si continúa enfocando programáticamente (digamos en un evento de doble toque) y deja que el usuario se aleje manualmente, después de un tiempo la diferencia entre la escala real y la escala UIScrollView es un seguimiento que crecerá demasiado, por lo que la escala previousScale
caerá en algún momento fuera de la precisión del flotador, lo que eventualmente dará como resultado un comportamiento impredecible.
Un enfoque bastante confiable, apropiado para iOS 3.2 y 4.0 y posteriores, es el siguiente. Debe estar preparado para proporcionar su vista escalable (la subvista principal de la vista de desplazamiento) en cualquier escala solicitada. Luego, en scrollViewDidEndZooming:
eliminará la versión de esta vista borrosa transformada a escala y la reemplazará con una nueva vista dibujada en la nueva escala.
Necesitará:
Un ivar para mantener la escala anterior; llamémoslo oldScale
Una forma de identificar la vista escalable, tanto para
viewForZoomingInScrollView:
como parascrollViewDidEndZooming:
aquí, voy a aplicar una etiqueta (999)Un método que crea la vista escalable, la etiqueta y la inserta en la vista de desplazamiento (aquí la llamaré
addNewScalableViewAtScale:
. Recuerde, debe hacer todo de acuerdo con la escala: dimensiona la vista y sus subvistas o dibujos, y ajusta el tamaño del contenido de la vista de desplazamiento para que coincida.Constantes para el mínimoZoomScale y maximumZoomScale. Estos son necesarios porque cuando reemplazamos la vista en escala por la versión más grande y detallada, el sistema va a pensar que la escala actual es 1, por lo que debemos engañar para permitir al usuario escalar la vista hacia abajo.
Muy bien entonces. Cuando crea la vista de desplazamiento, inserta la vista escalable en la escala 1.0. Esto podría estar en su viewDidLoad
, por ejemplo ( sv
es la vista de desplazamiento):
[self addNewScalableViewAtScale: 1.0];
sv.minimumZoomScale = MIN;
sv.maximumZoomScale = MAX;
self->oldScale = 1.0;
sv.delegate = self;
Luego, en su scrollViewDidEndZooming:
hace lo mismo pero compensa el cambio de escala:
UIView* v = [sv viewWithTag:999];
[v removeFromSuperview];
CGFloat newscale = scale * self->oldScale;
self->oldScale = newscale;
[self addNewScalableViewAtScale:newscale];
sv.minimumZoomScale = MIN / newscale;
sv.maximumZoomScale = MAX / newscale;
No puedo ayudarte con el bloqueo, aparte de decirte que verifiques y asegúrate de que no estás autorrealizando involuntariamente una vista o capa en algún lugar dentro de tu código. He visto que el simulador maneja el tiempo de las autorreleas de forma diferente que en el dispositivo (más a menudo cuando hay hilos involucrados).
Sin embargo, la escala de la vista es un problema con UIScrollView
. Durante un evento de acercamiento, UIScrollView
tomará la vista que especificó en el método viewForZoomingInScrollView:
delegate y le aplicará una transformación. Esta transformación proporciona una escala suave de la vista sin tener que volver a dibujar cada cuadro. Al final de la operación de zoom, se scrollViewDidEndZooming:withView:atScale:
su método de delegado scrollViewDidEndZooming:withView:atScale:
y le dará la oportunidad de realizar una representación de más alta calidad de su vista en el nuevo factor de escala. En general, se sugiere que restablezca la transformación en su vista para que sea CGAffineTransformIdentity
y luego CGAffineTransformIdentity
su vista se CGAffineTransformIdentity
dibujar manualmente en la nueva escala de tamaño.
Sin embargo, esto causa un problema porque UIScrollView
no parece controlar la transformación de la vista de contenido, por lo que en la siguiente operación de zoom establece la transformación de la vista de contenido a cualquiera que sea el factor de escala global. Como ha vuelto a dibujar manualmente su vista en el último factor de escala, aumenta la escala, que es lo que está viendo.
Como solución, utilizo una subclase UIView para mi vista de contenido con los siguientes métodos definidos:
- (void)setTransformWithoutScaling:(CGAffineTransform)newTransform;
{
[super setTransform:newTransform];
}
- (void)setTransform:(CGAffineTransform)newValue;
{
[super setTransform:CGAffineTransformScale(newValue, 1.0f / previousScale, 1.0f / previousScale)];
}
donde previousScale es una variable de instancia flotante de la vista. Luego implemento el método de delegar zoom de la siguiente manera:
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale;
{
[contentView setTransformWithoutScaling:CGAffineTransformIdentity];
// Code to manually redraw view at new scale here
contentView.previousScale = scale;
scrollView.contentSize = contentView.frame.size;
}
Al hacer esto, las transformaciones enviadas a la vista de contenido se ajustan en función de la escala en la que la vista se redibujó por última vez. Cuando se realiza el zoom de pellizco, la transformación se restablece a una escala de 1.0 al eludir el ajuste en el método setTransform:
normal. Esto parece proporcionar el comportamiento correcto de escalado al mismo tiempo que le permite dibujar una vista nítida al completar un zoom.
ACTUALIZACIÓN (23/7/2010): iPhone OS 3.2 y superior han cambiado el comportamiento de las vistas de desplazamiento con respecto al zoom. Ahora, un UIScrollView
respetará la transformación de identidad que aplique a una vista de contenido y solo proporcionará el factor de escala relativo en -scrollViewDidEndZooming:withView:atScale:
Por lo tanto, el código anterior para una subclase de UIView
solo es necesario para dispositivos con versiones de iPhone OS anteriores a la versión 3.2.
Gracias a todas las respuestas anteriores, y aquí está mi solución.
Implementar los métodos UIScrollViewDelegate :
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
{
return tmv;
}
- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(float)scale
{
CGPoint contentOffset = [tmvScrollView contentOffset];
CGSize contentSize = [tmvScrollView contentSize];
CGSize containerSize = [tmv frame].size;
tmvScrollView.maximumZoomScale = tmvScrollView.maximumZoomScale / scale;
tmvScrollView.minimumZoomScale = tmvScrollView.minimumZoomScale / scale;
previousScale *= scale;
[tmvScrollView setZoomScale:1.0f];
[tmvScrollView setContentOffset:contentOffset];
[tmvScrollView setContentSize:contentSize];
[tmv setFrame:CGRectMake(0, 0, containerSize.width, containerSize.height)];
[tmv reloadData];
}
- tmv es mi subclase de UIView
- tmvScrollView - salida a UIScrollView
- establecer maximumZoomScale y minimumZoomScale antes
- crear la variable de instancia de previousScale y establecer su valor en 1.0f
Funciona para mí perfectamente
BR, Eugene.