Cómo puedo auto dimensionar un UIScrollView para que se ajuste al contenido


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

Algo como:

[scrollView setContentSize:(CGSizeMake(320, content.height))];
Author: jwerre, 2010-05-31

20 answers

El mejor método que he encontrado para actualizar el tamaño del contenido de un UIScrollView basado en sus subviews contenidas:

ObjC

CGRect contentRect = CGRectZero;

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

Swift

  var contentRect = CGRect.zero

  for view in mainScrollView.subviews {
     contentRect = contentRect.union(view.frame)
  }
  mainScrollView.contentSize = contentRect.size
 277
Author: leviathan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-05 06:03:09

UIScrollView no conoce la altura de su contenido automáticamente. Debe calcular la altura y el ancho por sí 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 tienes una vista una al lado de la otra solo tienes que añadir la altura de una si no quieres establecer el contenido del scroller más grande de lo que realmente es.

 74
Author: emenegro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-18 13:56:05

He añadido esto a la respuesta de Espuz y JCC. Utiliza la posición y de las subvistas y no incluye las barras de desplazamiento. Edit Utiliza la parte inferior de la vista secundaria más baja que es 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;
}
 35
Author: richy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-25 23:01:54

Solución si está utilizando auto layout:

  • Establezca translatesAutoresizingMaskIntoConstraints a NO en todas las vistas involucradas.

  • Posiciona y dimensiona la vista de desplazamiento con restricciones externas a la vista de desplazamiento.

  • Use restricciones para diseñar las subviews dentro de la vista de desplazamiento, asegurándose de que las restricciones se vinculen a los cuatro bordes de la vista de desplazamiento y no confíen en la vista de desplazamiento para obtener sus Tamaño.

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

 32
Author: Adam Waite,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-27 22:17:59

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
 13
Author: Josh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-16 07:33:47

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 se establezcan ocultos.

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.

 6
Author: gokhanakkurt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-10 19:54:50

Excelente y mejor solución de @leviathan. Simplemente traduciendo a swift usando el enfoque FP (functional programming).

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect(), { 
  CGRectUnion($0, $1.frame) 
}.size
 5
Author: othierry42,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-27 14:11:33

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 fácil de usar !

 5
Author: fredericdnd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-08 23:52:15

Puede obtener la altura del contenido dentro de UIScrollView calculando qué hijo "alcanza más lejos". Para calcular esto hay que 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 (subviews) no tienen que apilarse directamente uno debajo de otro.

 4
Author: paxx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-21 18:43:15

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, averiguamos qué elemento está más abajo en la vista y agregamos un relleno de 10px a la parte inferior

 3
Author: Chris,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-05 16:12:53

Debido a que una vista de desplazamiento puede tener otras vistas de desplazamiento o diferentes subViews en profundidad, 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()
 3
Author: iluvatar_GR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-22 17:02:51

O simplemente hacer:

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 para este comentario, he decidido agregar esta solución como una respuesta formal para el beneficio de la comunidad!)

 2
Author: Gal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-15 08:21:44

El tamaño depende del contenido cargado en su interior y de las opciones de recorte. Si es una vista de texto, entonces también depende de la envoltura, cuántas líneas de texto, el tamaño de la fuente, y así sucesivamente. Casi imposible para calcular usted mismo. La buena noticia es que se calcula después de que se carga la vista y en el 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 contenido el tamaño será el real.

 1
Author: PapaSmurf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-05-27 03:25:51

Empaquetando el código de Richy He creado una clase UIScrollView personalizada que automatiza redimensionamiento del contenido completamente!

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

Modo de empleo:
Simplemente importa el .h archivo a su controlador de vista y declare una instancia SBScrollView en lugar de la normal UIScrollView.

 1
Author: AmitP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-02-19 20:15:26

Establezca el tamaño del contenido dinámico de esta manera.

 self.scroll_view.contentSize = CGSizeMake(screen_width,CGRectGetMaxY(self.controlname.frame)+20);
 1
Author: Monika Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-16 08:03:23

Depende del contenido realmente: contenido.marco.¿la altura podría darte lo que quieres ? Depende si el contenido es una sola cosa, o una colección de cosas.

 0
Author: Andiih,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-05-31 15:12:46

También encontré que la respuesta de leviatán funciona mejor. Sin embargo, calculaba una altura extraña. Al recorrer las subviews, si la vista de desplazamiento está configurada para mostrar indicadores de desplazamiento, estos estarán en la matriz de subviews. En este caso, la solución consiste en desactivar temporalmente los indicadores de desplazamiento antes de realizar el bucle y, a continuación, 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;
}
 0
Author: clayzar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-01-14 17:23:53

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
    }
}
 0
Author: Furqan Khan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-12 11:50:44

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

_yourScrollView.contentSize = CGSizeMake(0, _lastView.frame.origin.y + _lastView.frame.size.height);
 0
Author: Ali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-29 08:12:21

Para swift4 usando reduce:

self.scrollView.contentSize = self.scrollView.subviews.reduce(CGRect.zero, {
   return $0.union($1.frame)
}).size
 0
Author: mm282,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-22 12:26:48