Mantenga la image con zoom en el centro de UIScrollView

En mi aplicación de iPhone, necesito proporcionar al usuario la capacidad de acercar / desplazar una image de gran tamaño en la pantalla. Esto es bastante simple: utilizo UIScrollView , establece los factores de escala máxima / min y el zoom / paneo funciona como se esperaba. Aquí es donde las cosas se ponen interesantes. La image es dinámica, recibida de un server. Puede tener cualquier dimensión. Cuando la image se carga por primera vez, se networkinguce a escala (si es necesario) para encajar completamente en UIScrollView y está cinput en la vista de desplazamiento; la captura de pantalla se muestra a continuación:

introduzca la descripción de la imagen aquí

Debido a que las proporciones de la image son diferentes de las de la vista de desplazamiento, se agrega espacio en blanco arriba y debajo de la image para que la image esté cinput. Sin embargo, cuando empiezo a hacer un zoom de la image, la image real se vuelve lo suficientemente grande como para llenar toda la window gráfica de scrollview, por lo tanto, los rellenos blancos en la parte superior / inferior ya no son necesarios, sin embargo permanecen allí, como se puede ver en esta captura de pantalla:

introduzca la descripción de la imagen aquí

Creo que esto se debe al hecho de que el UIImageView contiene la image se dimensiona automáticamente para llenar todo el UIScrollView y cuando se amplía, simplemente crece proporcionalmente. Tiene el modo de escala configurado en Aspect Fit . La vista de delegado de viewForZoomingInScrollView simplemente devuelve la vista de la image.

scrollViewDidEndZooming recalcular y volver a establecer el tamaño de la vista UIScrollView , contentSize y la vista de la image en el método scrollViewDidEndZooming :

 CGSize imgViewSize = imageView.frame.size; CGSize imageSize = imageView.image.size; CGSize realImgSize; if(imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height) { realImgSize = CGSizeMake(imgViewSize.width, imgViewSize.width / imageSize.width * imageSize.height); } else { realImgSize = CGSizeMake(imgViewSize.height / imageSize.height * imageSize.width, imgViewSize.height); } scrollView.contentSize = realImgSize; CGRect fr = CGRectMake(0, 0, 0, 0); fr.size = realImgSize; imageView.frame = fr; 

Sin embargo, esto solo empeoraba las cosas (con los límites todavía allí, pero el outlook no funciona en la dirección vertical).

¿Hay alguna forma de networkingucir automáticamente ese espacio en blanco cuando se hace innecesario y luego boost nuevamente durante el acercamiento? Sospecho que el trabajo tendrá que hacerse en scrollViewDidEndZooming , pero no estoy muy seguro de qué debe ser ese código.

¡Increíble!

Gracias por el codigo 🙂

Solo pensé que lo añadiría a esto, ya que lo cambié un poco para mejorar el comportamiento.

 // make the change during scrollViewDidScroll instead of didEndScrolling... -(void)scrollViewDidZoom:(UIScrollView *)scrollView { CGSize imgViewSize = self.imageView.frame.size; CGSize imageSize = self.imageView.image.size; CGSize realImgSize; if(imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height) { realImgSize = CGSizeMake(imgViewSize.width, imgViewSize.width / imageSize.width * imageSize.height); } else { realImgSize = CGSizeMake(imgViewSize.height / imageSize.height * imageSize.width, imgViewSize.height); } CGRect fr = CGRectMake(0, 0, 0, 0); fr.size = realImgSize; self.imageView.frame = fr; CGSize scrSize = scrollView.frame.size; float offx = (scrSize.width > realImgSize.width ? (scrSize.width - realImgSize.width) / 2 : 0); float offy = (scrSize.height > realImgSize.height ? (scrSize.height - realImgSize.height) / 2 : 0); // don't animate the change. scrollView.contentInset = UIEdgeInsetsMake(offy, offx, offy, offx); } 

Aquí está mi solución que funciona universalmente con cualquier barra de tabs o combinación de barra de navigation o sin ambos, translúcidos o no.

 - (void)scrollViewDidZoom:(UIScrollView *)scrollView { // The scroll view has zoomed, so you need to re-center the contents CGSize scrollViewSize = [self scrollViewVisibleSize]; // First assume that image center coincides with the contents box center. // This is correct when the image is bigger than scrollView due to zoom CGPoint imageCenter = CGPointMake(self.scrollView.contentSize.width/2.0, self.scrollView.contentSize.height/2.0); CGPoint scrollViewCenter = [self scrollViewCenter]; //if image is smaller than the scrollView visible size - fix the image center accordingly if (self.scrollView.contentSize.width < scrollViewSize.width) { imageCenter.x = scrollViewCenter.x; } if (self.scrollView.contentSize.height < scrollViewSize.height) { imageCenter.y = scrollViewCenter.y; } self.imageView.center = imageCenter; } //return the scroll view center - (CGPoint)scrollViewCenter { CGSize scrollViewSize = [self scrollViewVisibleSize]; return CGPointMake(scrollViewSize.width/2.0, scrollViewSize.height/2.0); } // Return scrollview size without the area overlapping with tab and nav bar. - (CGSize) scrollViewVisibleSize { UIEdgeInsets contentInset = self.scrollView.contentInset; CGSize scrollViewSize = CGRectStandardize(self.scrollView.bounds).size; CGFloat width = scrollViewSize.width - contentInset.left - contentInset.right; CGFloat height = scrollViewSize.height - contentInset.top - contentInset.bottom; return CGSizeMake(width, height); } 

Por qué es mejor que cualquier otra cosa que pude encontrar en TAN hasta ahora:

  1. No lee ni modifica la propiedad del marco UIView de la vista de la image ya que una vista de la image ampliada tiene una transformación aplicada a ella. Vea aquí lo que Apple dice sobre cómo mover o ajustar un tamaño de vista cuando se aplica una transformación que no sea de identidad.

  2. A partir de iOS 7, donde se introdujo la translucidez para las barras, el sistema ajustará automáticamente el tamaño de la vista de desplazamiento, desplazará las inserciones de contenido e desplazamiento de los indicadores de desplazamiento. Por lo tanto, tampoco debe modificarlos en su código.

FYI: Hay casillas de verificación para alternar este comportamiento (que se establece de manera pnetworkingeterminada) en el generador de interfaces Xcode. Puede encontrarlo en los attributes del controller de vista:

ajustes de vista de desplazamiento automático

El código fuente del controller de vista completa se publica aquí .

También puede download todo el proyecto Xcode para ver la configuration de las restricciones de la vista de desplazamiento y jugar con 3 presets diferentes en el guión gráfico moviendo el puntero del controller inicial a cualquiera de las siguientes routes:

  1. Ver con pestaña translúcida y barras de navigation.
  2. Ver con pestaña opaca y barras de navigation.
  3. Ver sin barras en absoluto.

Cada opción funciona correctamente con la misma implementación de VC.

Creo que lo tengo. La solución es utilizar el método scrollViewDidEndZooming del delegado y, en ese método, establecer contentInset function del tamaño de la image. A continuación, se ve cómo se ve el método:

 - (void)scrollViewDidEndZooming:(UIScrollView *)aScrollView withView:(UIView *)view atScale:(float)scale { CGSize imgViewSize = imageView.frame.size; CGSize imageSize = imageView.image.size; CGSize realImgSize; if(imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height) { realImgSize = CGSizeMake(imgViewSize.width, imgViewSize.width / imageSize.width * imageSize.height); } else { realImgSize = CGSizeMake(imgViewSize.height / imageSize.height * imageSize.width, imgViewSize.height); } CGRect fr = CGRectMake(0, 0, 0, 0); fr.size = realImgSize; imageView.frame = fr; CGSize scrSize = scrollView.frame.size; float offx = (scrSize.width > realImgSize.width ? (scrSize.width - realImgSize.width) / 2 : 0); float offy = (scrSize.height > realImgSize.height ? (scrSize.height - realImgSize.height) / 2 : 0); [UIView beginAnimations:nil context:nil]; [UIView setAnimationDuration:0.25]; scrollView.contentInset = UIEdgeInsetsMake(offy, offx, offy, offx); [UIView commitAnimations]; } 

Tenga en count que estoy usando animation al configurar el recuadro; de lo contrario, la image salta dentro de la recuadro cuando se agregan las recuadros. Con animation se desliza hacia el centro. Estoy usando UIView beginAnimation y commitAnimation lugar del bloque de animation, porque necesito que la aplicación se ejecute en el iPhone 3.

Aquí está la versión rápida 3 de la respuesta de Genk

  func scrollViewDidZoom(_ scrollView: UIScrollView){ let imgViewSize:CGSize! = self.imageView.frame.size; let imageSize:CGSize! = self.imageView.image?.size; var realImgSize : CGSize; if(imageSize.width / imageSize.height > imgViewSize.width / imgViewSize.height) { realImgSize = CGSize(width: imgViewSize.width,height: imgViewSize.width / imageSize.width * imageSize.height); } else { realImgSize = CGSize(width: imgViewSize.height / imageSize.height * imageSize.width, height: imgViewSize.height); } var fr:CGRect = CGRect.zero fr.size = realImgSize; self.imageView.frame = fr; let scrSize:CGSize = scrollView.frame.size; let offx:CGFloat = (scrSize.width > realImgSize.width ? (scrSize.width - realImgSize.width) / 2 : 0); let offy:CGFloat = (scrSize.height > realImgSize.height ? (scrSize.height - realImgSize.height) / 2 : 0); scrollView.contentInset = UIEdgeInsetsMake(offy, offx, offy, offx); // The scroll view has zoomed, so you need to re-center the contents let scrollViewSize:CGSize = self.scrollViewVisibleSize(); // First assume that image center coincides with the contents box center. // This is correct when the image is bigger than scrollView due to zoom var imageCenter:CGPoint = CGPoint(x: self.scrollView.contentSize.width/2.0, y: self.scrollView.contentSize.height/2.0); let scrollViewCenter:CGPoint = self.scrollViewCenter() //if image is smaller than the scrollView visible size - fix the image center accordingly if (self.scrollView.contentSize.width < scrollViewSize.width) { imageCenter.x = scrollViewCenter.x; } if (self.scrollView.contentSize.height < scrollViewSize.height) { imageCenter.y = scrollViewCenter.y; } self.imageView.center = imageCenter; } //return the scroll view center func scrollViewCenter() -> CGPoint { let scrollViewSize:CGSize = self.scrollViewVisibleSize() return CGPoint(x: scrollViewSize.width/2.0, y: scrollViewSize.height/2.0); } // Return scrollview size without the area overlapping with tab and nav bar. func scrollViewVisibleSize() -> CGSize{ let contentInset:UIEdgeInsets = self.scrollView.contentInset; let scrollViewSize:CGSize = self.scrollView.bounds.standardized.size; let width:CGFloat = scrollViewSize.width - contentInset.left - contentInset.right; let height:CGFloat = scrollViewSize.height - contentInset.top - contentInset.bottom; return CGSize(width:width, height:height); } 

Aquí hay una extensión probada en Swift 3.1 . Simplemente cree un file * .swift por separado y pegue el código a continuación:

 import UIKit extension UIScrollView { func applyZoomToImageView() { guard let imageView = delegate?.viewForZooming?(in: self) as? UIImageView else { return } guard let image = imageView.image else { return } guard imageView.frame.size.valid && image.size.valid else { return } let size = image.size ~> imageView.frame.size imageView.frame.size = size self.contentInset = UIEdgeInsets( x: self.frame.size.width ~> size.width, y: self.frame.size.height ~> size.height ) imageView.center = self.contentCenter if self.contentSize.width < self.visibleSize.width { imageView.center.x = self.visibleSize.center.x } if self.contentSize.height < self.visibleSize.height { imageView.center.y = self.visibleSize.center.y } } private var contentCenter: CGPoint { return CGPoint(x: contentSize.width / 2, y: contentSize.height / 2) } private var visibleSize: CGSize { let size: CGSize = bounds.standardized.size return CGSize( width: size.width - contentInset.left - contentInset.right, height: size.height - contentInset.top - contentInset.bottom ) } } fileprivate extension CGFloat { static func ~>(lhs: CGFloat, rhs: CGFloat) -> CGFloat { return lhs > rhs ? (lhs - rhs) / 2 : 0.0 } } fileprivate extension UIEdgeInsets { init(x: CGFloat, y: CGFloat) { self.bottom = y self.left = x self.right = x self.top = y } } fileprivate extension CGSize { var valid: Bool { return width > 0 && height > 0 } var center: CGPoint { return CGPoint(x: width / 2, y: height / 2) } static func ~>(lhs: CGSize, rhs: CGSize) -> CGSize { switch lhs > rhs { case true: return CGSize(width: rhs.width, height: rhs.width / lhs.width * lhs.height) default: return CGSize(width: rhs.height / lhs.height * lhs.width, height: rhs.height) } } static func >(lhs: CGSize, rhs: CGSize) -> Bool { return lhs.width / lhs.height > rhs.width / rhs.height } } 

La forma de usar:

 extension YOUR_SCROLL_VIEW_DELEGATE: UIScrollViewDelegate { func viewForZooming(in scrollView: UIScrollView) -> UIView? { return YOUR_IMAGE_VIEW } func scrollViewDidZoom(_ scrollView: UIScrollView){ scrollView.applyZoomToImageView() } } 
    Intereting Posts