¿Cómo reinicio después de un zoom UIScrollView?

votos
15

Tengo un gráfico dibujado dentro de a UIScrollView. Es uno grande que UIViewusa una subclase personalizada CATiledLayercomo su capa.

Cuando hago un acercamiento y alejamiento de la imagen UIScrollView, quiero que el gráfico cambie de tamaño dinámicamente como lo hace cuando devuelvo el gráfico 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 una EXC_BAD_ACCSESen 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.

Publicado el 15/01/2009 a las 21:16
fuente por usuario
En otros idiomas...                            


7 respuestas

votos
41

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 UIScrollViewembargo, la escala de la vista es un problema con el que me he topado. Durante un evento de zoom pellizco, UIScrollViewtomará la vista que especificó en el viewForZoomingInScrollView:método de delegado 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, scrollViewDidEndZooming:withView:atScale:se llamará a su método de delegado y le dará la oportunidad de hacer una representación de su vista más de alta calidad con el nuevo factor de escala. En general, se sugiere que restablezca la transformación en su vista para que sea CGAffineTransformIdentityy luego haga que su vista se vuelva a dibujar manualmente en la nueva escala de tamaño.

Sin embargo, esto causa un problema porque UIScrollViewno 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 en 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 setTransform:método 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, UIScrollViewrespetará la transformación de identidad que aplica a una vista de contenido y solo proporciona el factor de escala relativo -scrollViewDidEndZooming:withView:atScale:. Por lo tanto, el código anterior para una UIViewsubclase solo es necesario para dispositivos que ejecutan versiones de iPhone OS anteriores a la versión 3.2.

Respondida el 16/01/2009 a las 20:29
fuente por usuario

votos
12

Gracias a todas las respuestas anteriores, y aquí está mi solución.
Implementar UIScrollViewDelegate métodos:

- (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 para UIScrollView
  • establecer maximumZoomScale y minimumZoomScale antes
  • crear previousScale variable de instancia y establezca su valor en 1.0f

A mí me funciona perfectamente.

BR, de Eugene.

Respondida el 02/08/2011 a las 17:33
fuente por usuario

votos
5

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 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 indocumentados como zoomScaley 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.

Respondida el 08/05/2009 a las 00:04
fuente por usuario

votos
4

Estaba mirando a restablecer la carga a los valores predeterminados de zoom, porque cuando zúmbalo, ScrollView está teniendo misma escala de zoom para la próxima vez hasta que se cancela la asignación.

-(void) viewWillAppear:(BOOL)animated {
      [self.scrollView setZoomScale:1.0f];
}

Cambiado el juego.

Respondida el 03/06/2015 a las 06:53
fuente por usuario

votos
3

Una aproximación bastante fiable, adecuada a iOS 3.2 y 4.0 y posterior, es el siguiente. Usted debe estar preparado para suministrar la vista escalable (el jefe subvista de la vista de desplazamiento) en cualquier escala requerida. Luego, en scrollViewDidEndZooming:que se eliminará la versión borrosa escala transformada de este punto de vista y sustituirla por una nueva visión dibujado a la nueva escala.

Necesitará:

  • Un Ivar para el mantenimiento de la escala anterior; vamos a llamarlo oldScale

  • Una manera de identificar la vista escalable, tanto para viewForZoomingInScrollView:y para scrollViewDidEndZooming:; aquí, voy a aplicar una etiqueta (999)

  • Un método que crea la vista escalable, lo etiqueta, y lo inserta en la vista de desplazamiento (aquí está lo llamaré addNewScalableViewAtScale:). Recuerde, tiene que hacer todo de acuerdo con la escala - es el tamaño de la vista y sus subvistas o dibujo y tamaños contentSize de la vista de desplazamiento para igualar.

  • Constantes del minimumZoomScale y maximumZoomScale. Estos cambios son necesarios porque cuando se sustituye la vista amplificada por la versión detalla más grande, el sistema va a pensar que la escala actual es 1, por lo que debemos engañar en que permite al usuario escalar la vista de nuevo hacia abajo.

Muy bien entonces. Cuando se crea la vista de desplazamiento, se inserta la vista escalable a escala de 1.0. Esto podría estar en su viewDidLoad, por ejemplo ( sves la vista de desplazamiento):

[self addNewScalableViewAtScale: 1.0];
sv.minimumZoomScale = MIN;
sv.maximumZoomScale = MAX;
self->oldScale = 1.0;
sv.delegate = self;

Luego, en el scrollViewDidEndZooming:que hace lo mismo, pero para compensar 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;
Respondida el 12/11/2010 a las 06:33
fuente por usuario

votos
2

Sin embargo, tenga en cuenta que la solución provista por Brad tiene un problema: si continúa enfocando programáticamente (digamos en el 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 previousScalevoluntad en algún momento caerá fuera de la precisión del flotador, lo que eventualmente dará como resultado un comportamiento impredecible

Respondida el 28/07/2009 a las 04:43
fuente por usuario

votos
0

Swift 4. * y Xcode 9.3

Ajuste de zoom ScrollView a 0 restablecerá su ScrollView a su estado inicial.

self.scrollView.setZoomScale(0.0, animated: true)
Respondida el 29/06/2018 a las 05:21
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more