Cómo tamaño automáticamente un UIScrollView para adaptarse al contenido

¿Hay alguna forma de hacer que un UIScrollView ajuste automáticamente a la altura (o ancho) del contenido que está desplazándose?

Algo como:

 [scrollView setContentSize:(CGSizeMake(320, content.height))]; 

El mejor método que he encontrado para actualizar el tamaño de contenido de un UIScrollView function de sus subvenciones contenidas:

 CGRect contentRect = CGRectZero; for (UIView *view in self.scrollView.subviews) { contentRect = CGRectUnion(contentRect, view.frame); } self.scrollView.contentSize = contentRect.size; 

UIScrollView no conoce la altura de su contenido automáticamente. Debes calcular la altura y el ancho por ti mismo

Hazlo con algo como

 CGFloat scrollViewHeight = 0.0f; for (UIView* view in scrollView.subviews) { scrollViewHeight += view.frame.size.height; } [scrollView setContentSize:(CGSizeMake(320, scrollViewHeight))]; 

Pero esto solo funciona si las vistas están una debajo de la otra. Si tiene una vista al lado de la otra, solo tiene que agregar la altura de uno si no desea configurar el contenido del desplazador más grande de lo que realmente es.

Agregué esto a la respuesta de Espuz y JCC. Utiliza la position y de las subvistas y no incluye las barras de desplazamiento. Editar Usa la parte inferior de la vista secundaria más baja que está visible.

 + (CGFloat) bottomOfLowestContent:(UIView*) view { CGFloat lowestPoint = 0.0; BOOL restreHorizontal = NO; BOOL restreVertical = NO; if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)]) { if ([(UIScrollView*)view showsHorizontalScrollIndicator]) { restreHorizontal = YES; [(UIScrollView*)view setShowsHorizontalScrollIndicator:NO]; } if ([(UIScrollView*)view showsVerticalScrollIndicator]) { restreVertical = YES; [(UIScrollView*)view setShowsVerticalScrollIndicator:NO]; } } for (UIView *subView in view.subviews) { if (!subView.hidden) { CGFloat maxY = CGRectGetMaxY(subView.frame); if (maxY > lowestPoint) { lowestPoint = maxY; } } } if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)]) { if (restreHorizontal) { [(UIScrollView*)view setShowsHorizontalScrollIndicator:YES]; } if (restreVertical) { [(UIScrollView*)view setShowsVerticalScrollIndicator:YES]; } } return lowestPoint; } 

Solución si está utilizando el layout automático:

  • Establecer translatesAutoresizingMaskIntoConstraints a NO en todas las vistas involucradas.

  • Coloque y dimensione su vista de desplazamiento con restricciones externas a la vista de desplazamiento.

  • Use restricciones para diseñar las subvistas dentro de la vista de desplazamiento, asegurándose de que las restricciones ata a los cuatro bordes de la vista de desplazamiento y no confíe en la vista de desplazamiento para get su tamaño.

Fuente: https://developer.apple.com/library/ios/technotes/tn2154/_index.html

Aquí está la respuesta aceptada en swift para cualquiera que sea demasiado perezoso para convertirlo 🙂

 var contentRect = CGRectZero for view in self.scrollView.subviews { contentRect = CGRectUnion(contentRect, view.frame) } self.scrollView.contentSize = contentRect.size 

La siguiente extensión sería útil en Swift .

 extension UIScrollView{ func setContentViewSize(offset:CGFloat = 0.0) { // dont show scroll indicators showsHorizontalScrollIndicator = false showsVerticalScrollIndicator = false var maxHeight : CGFloat = 0 for view in subviews { if view.hidden { continue } let newHeight = view.frame.origin.y + view.frame.height if newHeight > maxHeight { maxHeight = newHeight } } // set content size contentSize = CGSize(width: contentSize.width, height: maxHeight + offset) // show scroll indicators showsHorizontalScrollIndicator = true showsVerticalScrollIndicator = true } } 

La lógica es la misma con las respuestas dadas. Sin embargo, omite las vistas ocultas dentro de UIScrollView y el cálculo se realiza después de que los indicadores de desplazamiento estén ocultos.

Además, hay un parámetro de function opcional y puede agregar un valor de desplazamiento al pasar el parámetro a la function.

Aquí hay una adaptación Swift 3 de la respuesta de @ leviatan:

EXTENSIÓN

 import UIKit extension UIScrollView { func resizeScrollViewContentSize() { var contentRect = CGRect.zero for view in self.subviews { contentRect = contentRect.union(view.frame) } self.contentSize = contentRect.size } } 

USO

 scrollView.resizeScrollViewContentSize() 

Muy facil de usar

Puede get la altura del contenido dentro de UIScrollView calculando qué niño "llega más lejos". Para calcular esto, debe tener en count el origen Y (inicio) y la altura del elemento.

 float maxHeight = 0; for (UIView *child in scrollView.subviews) { float childHeight = child.frame.origin.y + child.frame.size.height; //if child spans more than current maxHeight then make it a new maxHeight if (childHeight > maxHeight) maxHeight = childHeight; } //set content size [scrollView setContentSize:(CGSizeMake(320, maxHeight))]; 

Al hacer las cosas de esta manera, los elementos (subvistas) no deben astackrse directamente uno debajo de otro.

Gran y mejor solución de @leviathan. Simplemente traduciendo a Swift utilizando el enfoque FP (functional programming).

 self.scrollView.contentSize = self.scrollView.subviews.networkinguce(CGRect(), { CGRectUnion($0, $1.frame) }.size 

Debido a que un scrollView puede tener otros scrollViews o diferentes subprocesss de DepthView, es preferible ejecutar en profundidad recursivamente. introduzca la descripción de la imagen aquí

Swift 2

 extension UIScrollView { //it will block the mainThread func recalculateVerticalContentSize_synchronous () { let unionCalculatedTotalRect = recursiveUnionInDepthFor(self) self.contentSize = CGRectMake(0, 0, self.frame.width, unionCalculatedTotalRect.height).size; } private func recursiveUnionInDepthFor (view: UIView) -> CGRect { var totalRect = CGRectZero //calculate recursevly for every subView for subView in view.subviews { totalRect = CGRectUnion(totalRect, recursiveUnionInDepthFor(subView)) } //return the totalCalculated for all in depth subViews. return CGRectUnion(totalRect, view.frame) } } 

Uso

 scrollView.recalculateVerticalContentSize_synchronous() 

Se me ocurrió otra solución basada en la solución de @ emenegro

 NSInteger maxY = 0; for (UIView* subview in scrollView.subviews) { if (CGRectGetMaxY(subview.frame) > maxY) { maxY = CGRectGetMaxY(subview.frame); } } maxY += 10; [scrollView setContentSize:CGSizeMake(scrollView.frame.size.width, maxY)]; 

Básicamente, descubrimos qué elemento está más abajo en la vista y agrega un relleno de 10 px al background

O simplemente hazlo:

 int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))]; 

(Esta solución fue agregada por mí como un comentario en esta página. Después de recibir 19 votos a favor para este comentario, ¡he decidido agregar esta solución como una respuesta formal para el beneficio de la comunidad!)

El tamaño depende del contenido cargado dentro de él y de las opciones de recorte. Si es una vista de text, entonces también depende del ajuste, cuántas líneas de text, el tamaño de fuente y así sucesivamente. Casi imposible para usted calcularse usted mismo. La buena noticia es que se calcula una vez que se carga la vista y en la vista Willappear. Antes de eso, es todo desconocido y el tamaño del contenido será el mismo que el tamaño del marco. Pero, en el método viewWillAppear y después (como el viewDidAppear), el tamaño del contenido será el real.

Envolviendo el código de Richy, creé una class UIScrollView personalizada que automatiza el cambio de tamaño del contenido por completo.

SBScrollView.h

 @interface SBScrollView : UIScrollView @end 

SBScrollView.m:

 @implementation SBScrollView - (void) layoutSubviews { CGFloat scrollViewHeight = 0.0f; self.showsHorizontalScrollIndicator = NO; self.showsVerticalScrollIndicator = NO; for (UIView* view in self.subviews) { if (!view.hidden) { CGFloat y = view.frame.origin.y; CGFloat h = view.frame.size.height; if (y + h > scrollViewHeight) { scrollViewHeight = h + y; } } } self.showsHorizontalScrollIndicator = YES; self.showsVerticalScrollIndicator = YES; [self setContentSize:(CGSizeMake(self.frame.size.width, scrollViewHeight))]; } @end 

Cómo utilizar:
Simplemente importe el file .h a su controller de vista y declare una instancia de SBScrollView en lugar del file UIScrollView normal.

Establezca un tamaño de contenido dynamic como este.

  self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20); 

Depende realmente del contenido: content.frame.height podría darte lo que quieres? Depende si el contenido es una sola cosa, o una colección de cosas.

También encontré la respuesta de Leviatán para trabajar mejor. Sin embargo, estaba calculando una altura extraña. Al recorrer las subvistas, si la vista de desplazamiento está configurada para mostrar los indicadores de desplazamiento, estarán en la matriz de subvistas. En este caso, la solución es deshabilitar temporalmente los indicadores de desplazamiento antes de hacer un bucle, y luego volver a establecer su configuration de visibilidad previa.

-(void)adjustContentSizeToFit es un método público en una subclass personalizada de UIScrollView.

 -(void)awakeFromNib { dispatch_async(dispatch_get_main_queue(), ^{ [self adjustContentSizeToFit]; }); } -(void)adjustContentSizeToFit { BOOL showsVerticalScrollIndicator = self.showsVerticalScrollIndicator; BOOL showsHorizontalScrollIndicator = self.showsHorizontalScrollIndicator; self.showsVerticalScrollIndicator = NO; self.showsHorizontalScrollIndicator = NO; CGRect contentRect = CGRectZero; for (UIView *view in self.subviews) { contentRect = CGRectUnion(contentRect, view.frame); } self.contentSize = contentRect.size; self.showsVerticalScrollIndicator = showsVerticalScrollIndicator; self.showsHorizontalScrollIndicator = showsHorizontalScrollIndicator; } 

Creo que esto puede ser una forma clara de actualizar el tamaño de vista de contenido de UIScrollView.

 extension UIScrollView { func updateContentViewSize() { var newHeight: CGFloat = 0 for view in subviews { let ref = view.frame.origin.y + view.frame.height if ref > newHeight { newHeight = ref } } let oldSize = contentSize let newSize = CGSize(width: oldSize.width, height: newHeight + 20) contentSize = newSize } } 

¿Por qué no una sola línea de código?

 _yourScrollView.contentSize = CGSizeMake(0, _lastView.frame.origin.y + _lastView.frame.size.height);