Respuestas:
El mejor método que he encontrado para actualizar el tamaño del contenido de un UIScrollView
basado en sus subvistas contenidas:
C objetivo
CGRect contentRect = CGRectZero;
for (UIView *view in self.scrollView.subviews) {
contentRect = CGRectUnion(contentRect, view.frame);
}
self.scrollView.contentSize = contentRect.size;
Rápido
let contentRect: CGRect = scrollView.subviews.reduce(into: .zero) { rect, view in
rect = rect.union(view.frame)
}
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 son una debajo de la otra. Si tiene una vista una al lado de la otra, solo tiene que agregar la altura de una si no desea establecer el contenido del desplazador más grande de lo que realmente es.
int y = CGRectGetMaxY(((UIView*)[_scrollView.subviews lastObject]).frame); [_scrollView setContentSize:(CGSizeMake(CGRectGetWidth(_scrollView.frame), y))];
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 estén vinculadas a los cuatro bordes de la vista de desplazamiento y no confíe en la vista de desplazamiento para obtener su tamaño.
Fuente: https://developer.apple.com/library/ios/technotes/tn2154/_index.html
Agregué esto a la respuesta de Espuz y JCC. Utiliza la posición y de las subvistas y no incluye las barras de desplazamiento. Editar Utiliza la parte inferior de la vista secundaria más baja que está visible.
+ (CGFloat) bottomOfLowestContent:(UIView*) view
{
CGFloat lowestPoint = 0.0;
BOOL restoreHorizontal = NO;
BOOL restoreVertical = NO;
if ([view respondsToSelector:@selector(setShowsHorizontalScrollIndicator:)] && [view respondsToSelector:@selector(setShowsVerticalScrollIndicator:)])
{
if ([(UIScrollView*)view showsHorizontalScrollIndicator])
{
restoreHorizontal = YES;
[(UIScrollView*)view setShowsHorizontalScrollIndicator:NO];
}
if ([(UIScrollView*)view showsVerticalScrollIndicator])
{
restoreVertical = 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 (restoreHorizontal)
{
[(UIScrollView*)view setShowsHorizontalScrollIndicator:YES];
}
if (restoreVertical)
{
[(UIScrollView*)view setShowsVerticalScrollIndicator:YES];
}
}
return lowestPoint;
}
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.showsVerticalScrollIndicator = NO;
al principio y self.scrollView.showsHorizontalScrollIndicator = YES;
self.scrollView.showsVerticalScrollIndicator = YES;
al final del método?
Aquí está la respuesta aceptada rápidamente para cualquier persona que sea demasiado perezosa para convertirla :)
var contentRect = CGRectZero
for view in self.scrollView.subviews {
contentRect = CGRectUnion(contentRect, view.frame)
}
self.scrollView.contentSize = contentRect.size
Aquí hay una adaptación de 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!
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.isHidden {
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 UIScrollView
y el cálculo se realiza después de que los indicadores de desplazamiento se ocultan.
Además, hay un parámetro de función opcional y puede agregar un valor de desplazamiento pasando el parámetro a la función.
Gran y mejor solución de @leviathan. Simplemente traduciendo a Swift usando el enfoque FP (programación funcional).
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), {
CGRectUnion($0, $1.frame)
}.size
self.contentSize = self.subviews.reduce(CGRect(), { $0.union($1.frame) }).size
Puede obtener la altura del contenido dentro de UIScrollView calculando qué hijo "llega más lejos". Para calcular esto, debe tener en cuenta el origen Y (inicio) y la altura del artículo.
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 tienen que apilarse directamente uno debajo del otro.
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 agregamos un relleno de 10px en la parte inferior
Debido a que scrollView puede tener otras scrollViews o diferentes inDepth subViews tree, es preferible ejecutarlo en profundidad de forma recursiva.
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()
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 obtener 19 votos positivos para este comentario, he decidido agregar esta solución como una respuesta formal para el beneficio de la comunidad!)
Para swift4 usando reduce:
self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
return $0.union($1.frame)
}).size
El tamaño depende del contenido cargado dentro de él y las opciones de recorte. Si se trata de una vista de texto, entonces también depende del ajuste, cuántas líneas de texto, el tamaño de fuente, etc. Casi imposible para usted computarse. La buena noticia es que se calcula después de cargar la vista y en viewWillAppear. Antes de eso, todo es 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 viewDidAppear), el tamaño del contenido será el real.
Envolviendo el código de Richy, ¡creé una clase UIScrollView personalizada que automatiza completamente el cambio de tamaño del contenido!
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 usarlo:
simplemente importe el archivo .h a su controlador de vista y declare una instancia de SBScrollView en lugar de la instancia normal de UIScrollView.
Establezca un tamaño de contenido dinámico como este.
self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
También encontré la respuesta del 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 indicadores de desplazamiento, estarán en la matriz de subvistas. En este caso, la solución es desactivar temporalmente los indicadores de desplazamiento antes de realizar un bucle y luego restablecer su configuración de visibilidad anterior.
-(void)adjustContentSizeToFit
es un método público en una subclase 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 esta puede ser una buena manera de actualizar el tamaño de la 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
}
}
import UIKit
class DynamicSizeScrollView: UIScrollView {
var maxHeight: CGFloat = UIScreen.main.bounds.size.height
var maxWidth: CGFloat = UIScreen.main.bounds.size.width
override func layoutSubviews() {
super.layoutSubviews()
if !__CGSizeEqualToSize(bounds.size,self.intrinsicContentSize){
self.invalidateIntrinsicContentSize()
}
}
override var intrinsicContentSize: CGSize {
let height = min(contentSize.height, maxHeight)
let width = min(contentSize.height, maxWidth)
return CGSize(width: width, height: height)
}
}
viewDidLayoutSubviews
para que el autolayout terminara, en iOS7 funcionó bien, pero probar os iOS6 el autolayout por alguna razón no terminó el trabajo, por lo que tenía algunos valores de altura incorrectos, así que cambié aviewDidAppear
ahora funciona bien ... solo para señalar que tal vez alguien necesitaría esto. gracias