Pregunta

Tengo un gráfico que se dibuja dentro de un UIScrollView . Es una gran UIView que utiliza una subclase personalizada de CATiledLayer como capa.

Cuando acerco y alejo el UIScrollView , quiero que el gráfico cambie de tamaño dinámicamente como lo hace cuando devuelvo el gráfico desde viewForZoomingInScrollView . Sin embargo, el Gráfico se vuelve a dibujar 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 hace zoom, la transformación se restablece a sí mismo al nivel de zoom en el que se encontraba, y parece que, si me acerqué a 2x, por ejemplo, de repente es 4x. Cuando termino el zoom, termina en la escala correcta, pero el acto real de hacer zoom se ve mal.

Entonces, primero: ¿cómo permito que el gráfico se vuelva a dibujar a la escala estándar de 1x1 después del zoom, y cómo tengo un zoom uniforme en todas partes?

Editar: Nuevos hallazgos El error parece ser " [CALayer retenciónCuenta]: mensaje enviado a la instancia desasignada "

Nunca voy a desasignar ninguna capa yo mismo. Antes, ni siquiera estaba borrando ninguna vista ni nada. Este error se estaba generando en el zoom y también en 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.

¿Fue útil?

Solución

No puedo ayudarte con el bloqueo, aparte de decirte que compruebes y asegúrate de que no estás autorrelegando involuntariamente una vista o capa en algún lugar dentro de tu código. He visto que el simulador maneja el tiempo de las autoreleases de manera diferente que en el dispositivo (con mayor frecuencia cuando hay hilos involucrados).

Sin embargo, la escala de la vista es un problema con UIScrollView . Durante un evento de pellizco con zoom, UIScrollView tomará la vista que especificó en el método de delegado viewForZoomingInScrollView: y le aplicará una transformación. Esta transformación proporciona una escala suave de la vista sin tener que volver a dibujarla en cada cuadro. Al final de la operación de zoom, se llamará a su método de delegado scrollViewDidEndZooming: withView: atScale: y le dará la oportunidad de hacer una representación de su vista de mayor calidad en el nuevo factor de escala. En general, se sugiere restablecer la transformación en su vista para que sea CGAffineTransformIdentity y luego hacer que su vista se vuelva a dibujar manualmente en la nueva escala de tamaño.

Sin embargo, esto causa un problema porque UIScrollView no parece monitorear 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 en el factor de escala general . Como ha redibujado manualmente su vista en el último factor de escala, agrava la escala, que es lo que está viendo.

Como solución alternativa, uso 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 delegado de 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 a la que se redibujó la vista por última vez. Cuando se realiza el pellizco de zoom, la transformación se restablece a una escala de 1.0 omitiendo el ajuste en el método normal setTransform: . Esto parece proporcionar el comportamiento de escala correcto mientras le permite dibujar una vista nítida al completar un zoom.

ACTUALIZACIÓN (23/07/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 aplica 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 UIView solo es necesario para dispositivos que ejecutan versiones de iPhone OS anteriores a 3.2.

Otros consejos

Gracias a todas las respuestas anteriores, y aquí está mi solución.
Implemente 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 & # 8212; salida a UIScrollView
  • establezca maximumZoomScale y minimumZoomScale antes
  • crea la variable de instancia previousScale y establece su valor en 1.0f

Funciona para mí perfectamente.

BR, Eugene.

Solo estaba buscando restablecer en la carga a la escala de zoom predeterminada, porque cuando la hago zoom, 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 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 hacer un zoom programático de UIScrollView, cómo emular la paginación + zooming + desplazamiento de Photo Library, un proyecto de ejemplo y una clase ZoomScrollView que encapsula parte de la magia del zoom).

Cita:

UIScrollView no tiene una noción de un & # 8220; nivel de zoom actual & # 8221 ;, porque 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 pellizca el zoom de una subvista, luego restablece su transformación a CGAffineTransformIdentity y luego vuelve a pellizcar, notará que el nivel de zoom anterior de la subvista se ha restaurado.

De hecho, si observa el desmontaje, es UIView que almacena su propio nivel de zoom (dentro del objeto UIGestureInfo señalado por el campo _gestureInfo). También tiene un conjunto de buenos métodos no documentados como zoomScale y setZoomScale: animated: . (Eso sí, también tiene un montón de métodos relacionados con la rotación, tal vez estemos recibiendo soporte para 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 elemento secundario, siempre comenzaremos con el nivel de zoom 1.0. Mi implementación del zoom programático se basa en este truco.

Un enfoque bastante confiable, apropiado para iOS 3.2 y 4.0 y posterior, 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 transformada de escala borrosa de esta vista y la reemplazará con una nueva vista dibujada a la nueva escala.

Necesitarás:

  • Un ivar para mantener la escala anterior; vamos a llamarlo oldScale

  • Una forma de identificar la vista escalable, tanto para viewForZoomingInScrollView: como para scrollViewDidEndZooming: ; 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: ajusta el tamaño de la vista y sus subvistas o dibujos, y ajusta el tamaño del contenido de la vista de desplazamiento para que coincida.

  • Constantes para minimumZoomScale y maximumZoomScale. Estos son necesarios porque cuando reemplazamos la vista escalada por la versión detallada más grande, el sistema pensará que la escala actual es 1, por lo que debemos engañarlo para que permita 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;

Tenga en cuenta que la solución proporcionada por Brad tiene un problema: si sigue haciendo zoom programáticamente (digamos en un evento de doble toque) y deja que el usuario se aleje manualmente (pellizcar), después de un tiempo la diferencia entre la escala real y la escala que UIScrollView está rastreando crecerá demasiado, por lo que previousScale en algún momento caerá fuera de la precisión del flotador, lo que eventualmente resultará en un comportamiento impredecible

Swift 4. * y Xcode 9.3

Establecer la escala de zoom scrollView en 0 restablecerá su scrollView a su estado inicial.

self.scrollView.setZoomScale(0.0, animated: true)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top