Diseño automático de iOS con UIScrollView: ¿Por qué la vista de contenido de la vista de desplazamiento no llena la vista de desplazamiento?


El siguiente código (llamado en viewDidLoad) resulta en una pantalla completamente roja. Esperaría que fuera una pantalla totalmente verde. ¿Por qué es rojo? ¿Y cómo puedo hacerlo todo verde?

UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];

UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView,contentView);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]];
Author: dragosaur, 2013-05-30

3 answers

Las restricciones con vistas de desplazamiento funcionan de forma ligeramente diferente que con otras vistas. Las restricciones entre contentView y su superview (el scrollView) son a la scrollView's contentSize, no a su frame. Esto puede parecer confuso, pero en realidad es bastante útil, lo que significa que nunca tendrá que ajustar el contentSize, sino que el contentSize se ajustará automáticamente para adaptarse a su contenido. Este comportamiento se describe en Nota Técnica TN2154.

Si desea definir el tamaño contentView a la pantalla o algo así, tendría que agregar una restricción entre la contentView y la vista principal, por ejemplo. Eso es, ciertamente, la antítesis de poner contenido en el scrollview, así que probablemente no aconsejaría eso, pero se puede hacer.


Para ilustrar este concepto, que el tamaño de contentView será impulsado por su contenido, no por el bounds del scrollView, agregue una etiqueta a su contentView:

UIScrollView* scrollView = [UIScrollView new];
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.backgroundColor = [UIColor redColor];
[self.view addSubview:scrollView];

UIView* contentView = [UIView new];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
contentView.backgroundColor = [UIColor greenColor];
[scrollView addSubview:contentView];

UILabel *randomLabel = [[UILabel alloc] init];
randomLabel.text = @"this is a test";
randomLabel.translatesAutoresizingMaskIntoConstraints = NO;
randomLabel.backgroundColor = [UIColor clearColor];
[contentView addSubview:randomLabel];

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" options:0 metrics:0 views:viewDict]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|" options:0 metrics:0 views:viewDict]];

[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[contentView]|" options:0 metrics:0 views:viewDict]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[contentView]|" options:0 metrics:0 views:viewDict]];

[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]];
[contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[randomLabel]-|" options:0 metrics:0 views:viewDict]];

Ahora verás que los contentView (y, por lo tanto, los contentSize de los scrollView) son ajustado para ajustarse a la etiqueta con márgenes estándar. Y porque no especificé el ancho / alto de la etiqueta, eso se ajustará en función del texto que pones en esa etiqueta.


Si desea que contentView también se ajuste al ancho de la vista principal, podría redefinir su viewDict de esta manera, y luego agregar estas restricciones adicionales (además de todas las demás, arriba):

UIView *mainView = self.view;

NSDictionary* viewDict = NSDictionaryOfVariableBindings(scrollView, contentView, randomLabel, mainView);

[mainView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:[contentView(==mainView)]" options:0 metrics:0 views:viewDict]];

Hay un problema conocido (bug?) con etiquetas multilínea en scrollviews, que si si quieres que cambie de tamaño de acuerdo con la cantidad de texto, tienes que hacer un poco de juego de manos, como:

dispatch_async(dispatch_get_main_queue(), ^{
    randomLabel.preferredMaxLayoutWidth = self.view.bounds.size.width;
});
 68
Author: Rob,
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-05-23 12:25:36

He intentado respuesta de Rob parece bien al principio. PERO! si también habilita zoom, este código de diseño automático se interpondrá en el camino. Sigue redimensionando el contentView cuando hace zoom. Es decir, si amplío (Escala de zoom > 1), no podré desplazar las partes fuera de la pantalla.

Después de días de lucha con Autolayout, lamentablemente no pude encontrar ninguna solución. Al final, ni siquiera importa (wat? ok, lo siento). Al final, acabo de eliminar el Diseño automático en contentView (use un contentView de tamaño fijo), y luego en layoutSubviews, ajusto el self.scrollView.contentInset. (Estoy usando Xcode5, iOS 7)

Sé que esto no es una solución directa a esta pregunta. Pero solo quiero señalar una solución fácil. Funciona perfectamente para centrar un contentView de tamaño fijo en un ScrollView, ¡con solo 2 líneas de código! Espero que pueda ayudar a alguien;)

- (void)layoutSubviews {
    [super layoutSubviews];

    // move contentView to center of scrollView by changing contentInset,
    // because I CANNOT set this with AUTOLAYOUT!!!
    CGFloat topInset = (self.frame.size.height - self.contentView.frame.size.height)/2;
    self.scrollView.contentInset = UIEdgeInsetsMake(topInset, 0, 0, 0);
}
 1
Author: Hlung,
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-02-14 18:25:04

En general, el Diseño automático considera que los bordes superior, izquierdo, inferior y derecho de una vista son los bordes visibles. Es decir, si anclas una vista al borde izquierdo de su superview, realmente la anclas al valor x mínimo de los límites de la superview. Cambiar el origen de los límites de la supervisión no cambia la posición de la vista.

La clase UIScrollView desplaza su contenido cambiando el origen de sus límites. Para hacer que esto funcione con el diseño automático, la parte superior, izquierda, inferior y derecha los bordes dentro de una vista de desplazamiento ahora significan los bordes de su vista de contenido.

Las restricciones en las subviews de la vista de desplazamiento deben resultar en un tamaño a rellenar, que luego se interpreta como el tamaño de contenido de la vista de desplazamiento. (Esto no debe confundirse con el método intrinsicContentSize utilizado para el Diseño automático.) Para dimensionar el marco de la vista de desplazamiento con Diseño automático, las restricciones deben ser explícitas con respecto al ancho y la altura de la vista de desplazamiento, o los bordes de la vista de desplazamiento deben estar vinculados a vistas fuera de su subárbol.

Tenga en cuenta que puede hacer que una vista secundaria de la vista de desplazamiento aparezca flotando (no desplazarse) sobre el otro contenido de desplazamiento creando restricciones entre la vista y una vista fuera del subárbol de la vista de desplazamiento, como la vista superior de la vista de desplazamiento.

Aquí hay dos ejemplos de cómo configurar la vista de desplazamiento, primero el enfoque mixto y luego el enfoque puro

 -1
Author: Afzal Siddiqui,
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-11-28 09:16:05