UIScrollView con escala UIImageView usando autolayout

UIScrollView con UIImageView

Considere un UIScrollView con una sola subvista. La subvista es un UIImageView con las siguientes restricciones de tamaño:

  • Su altura debe ser igual a la altura de UIScrollView .
  • Su ancho debe ser el ancho de la image escalada proporcionalmente a la altura de UIImageView .

Se espera que el ancho del UIImageView sea ​​mayor que el ancho del UIScrollView, de ahí la necesidad de desplazarse.

La image puede configurarse durante viewDidLoad (si está en caching) o de forma asíncrona.

¿Cómo implementas lo anterior utilizando la function de reproducción automática, haciendo el mayor uso posible del generador de interfaces?

Lo que he hecho hasta ahora

Sobre la base de esta respuesta , configuré mi plumilla como esta:

  • El UIScrollView está fijado a los bordes de su supervisión.
  • UIImageView está fijado a los bordes de UIScrollView .
  • UIImageView tiene un tamaño intrínseco de marcador de position (para evitar el error de ambigüedad de tamaño de contenido desplazable )

Como se esperaba, el resultado es que el UIImageView tiene el tamaño del tamaño del UIImage , y el UIScrollView desplaza horizontal y verticalmente (ya que la image es más grande que el UIScrollView ).

Luego probé varias cosas que no funcionaron:

  • Después de cargar la image, configure manualmente el marco de UIImageView .
  • Agregue una restricción para el ancho del UIImageView y modifique su valor después de que se haya cargado la image. Esto hace que la image sea aún más grande (?!).
  • Establezca zoomScale después de zoomScale la image. No tiene efecto visible.

Sin autolayout

El siguiente código hace exactamente lo que quiero, aunque sin autolayout o generador de interfaces.

 - (void)viewDidLoad { { UIScrollView *scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.view addSubview:scrollView]; self.scrollView = scrollView; } { UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.scrollView.frame.size.width, self.scrollView.frame.size.height)]; imageView.contentMode = UIViewContentModeScaleAspectFit; imageView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self.scrollView addSubview:imageView]; self.scrollView.contentSize = imageView.frame.size; self.imageView = imageView; } } - (void)viewDidLayoutSubviews { [super viewDidLayoutSubviews]; [self layoutStripImageView]; } - (void)layoutStripImageView { // Also called when the image finishes loading UIImage *image = self.imageView.image; if (! image) return; const CGSize imageSize = image.size; const CGFloat vh = self.scrollView.frame.size.height; const CGFloat scale = vh / imageSize.height; const CGFloat vw = imageSize.width * scale; CGSize imageViewSize = CGSizeMake(vw, vh); self.imageView.frame = CGRectMake(0, 0, imageViewSize.width, imageViewSize.height); self.scrollView.contentSize = imageViewSize; } 

Estoy intentando realmente moverme a la reproducción automática, pero no es fácil.

Bajo el régimen de reproducción automática, lo ideal es que el contenido de UIScrollView sea determinado únicamente por las restricciones y no se establezca explícitamente en el código.

Entonces en tu caso:

  • Cree restricciones para fijar la subview a UIScrollView . Las restricciones deben garantizar que el margen entre la subvista y la vista de desplazamiento sea 0. Veo que ya lo intentó.

  • Cree una altura y una restricción de ancho para su subvista. De lo contrario, el tamaño intrínseco de UIImageView determina su altura y ancho. En el momento del layout, este tamaño es solo un marcador de position para mantener Happy Builder. En time de ejecución, se establecerá en el tamaño real de la image, pero esto no es lo que desea.

  • Durante viewDidLayoutSubviews , actualice las restricciones para que sea el tamaño de contenido real. Puede hacer esto directamente cambiando la propiedad constant de la restricción de altura y anchura, o llamando a setNeedsUpdateConstraints y anulando updateConstraints para hacer lo mismo.

Esto garantiza que el sistema pueda derivar contentSize únicamente de las restricciones.

He hecho lo anterior y funciona de manera confiable en iOS 6 y 7 con un UIScrollView y una subvista personalizada, por lo que debería funcionar también para UIImageView . En particular, si no pincha la subvista en la vista de desplazamiento, el zoom será inquietante en iOS 6.

También puede intentar crear restricciones de altura y ancho que hagan reference directa a un múltiplo de la altura y el ancho de la vista de desplazamiento, pero no he probado este otro enfoque.

translatesAutoresizingMaskIntoConstraints solo es necesario cuando crea una instancia de la vista en el código. Si lo instalás en el IB, está deshabilitado por defecto

En mi opinión, UIImageView debería llenar el ScrollView. Más tarde intentaré ajustar el zoom de la vista de desplazamiento al valor que mejor se adapta a ti para que la image solo pueda ser desplazada en una dirección

En mi caso, era un UIImageView de ancho completo, tenía una restricción de altura definida que causaba el problema.

Puse otra restricción en el UIImageView para el ancho que coincidía con el ancho de UIScrollView tal como está en el generador de interfaces y luego agregué una salida al UIViewController:

 @property (weak, nonatomic) IBOutlet NSLayoutConstraint *imageViewWidthConstraint; 

luego en viewDidLayoutSubviews, actualicé la restricción:

 - (void) viewDidLayoutSubviews { [super viewDidLayoutSubviews]; self.imageViewWidthConstraint.constant = CGRectGetWidth(self.scrollView.frame); } 

Esto parecía hacer el truco.