Pregunta Autolayout de iOS Espacio verticalmente igual para llenar la vista principal


Tengo un controlador de vista con 12 UITextFields.

Se adapta muy bien a una pantalla de 3.5 ".

enter image description here

Necesito configurarlo para iPhone 5 (pantalla de 4 pulgadas) para que todos UITextFields cubrir todo UIView agregando espacio extra en el medio.

Estoy tratando de hacer esto por diseño automático, pero no está funcionando correctamente.

enter image description here

Este es mi código:

- (void) viewWillLayoutSubviews
{
    int h = txt1.bounds.size.height * 12;

    float unusedHorizontalSpace = self.view.bounds.size.height - h ;

    NSNumber* spaceBetweenEachButton=  [NSNumber numberWithFloat: unusedHorizontalSpace / 13 ] ;

    NSMutableArray *constraintsForButtons = [[NSMutableArray alloc] init];

    [constraintsForButtons addObjectsFromArray: [NSLayoutConstraint constraintsWithVisualFormat: @"V:|-50-[txt1(==30)]-(space)-[txt2(==txt1)]-(space)-[txt3(==txt1)]-(space)-[txt4(==txt1)]-(space)-[txt5(==txt1)]-(space)-[txt6(==txt1)]-(space)-[txt7(==txt1)]-(space)-[txt8(==txt1)]-(space)-[txt9(==txt1)]-(space)-[txt10(==txt1)]-(space)-[txt11(==txt1)]-(space)-[txt12]-(space)-|"
                                                                                        options: NSLayoutFormatAlignAllCenterX
                                                                                        metrics: @{@"space":spaceBetweenEachButton}
                                                                                          views: NSDictionaryOfVariableBindings(txt1,txt10,txt11,txt12,txt2,txt3,txt4,txt5,txt6, txt7,txt8,txt9)]];

    [self.view addConstraints:constraintsForButtons];
}

Si lo hago [txt12(==txt1)] luego muestra lo mismo que la pantalla de 3.5 "y deja espacio debajo.

Donde estoy cometiendo un error?


14
2018-06-13 14:16


origen


Respuestas:


Para hacer esto con el diseño automático, debe crear vistas adicionales para llenar los espacios entre los campos de texto.

Recuerde que una restricción de diseño automático es básicamente la ecuación lineal A = m * B + c. A es un atributo de una vista (por ejemplo, la coordenada Y de viewAel borde inferior) y B es un atributo de otra vista (por ejemplo, la coordenada Y de viewBel borde superior). m y c son constantes. Entonces, por ejemplo, para diseñar viewA y viewB de modo que hay 30 puntos entre la parte inferior viewA y la parte superior de viewB, podríamos crear una restricción donde m es 1 y c es -30.

El problema que tienes es que quieres usar el mismo valor para c a través de 13 restricciones diferentes, y desea que el diseño automático calcule c valor para ti El diseño automático simplemente no puede hacer eso. No directamente. El diseño automático solo puede calcular los atributos de las vistas; no puede calcular el m y c constantes.

Hay una manera de hacer que el diseño automático coloque las vistas donde desee: reificar los espacios entre los campos de texto como vistas adicionales (invisibles). Aquí hay un ejemplo con solo 3 campos de texto:

example layout

Crearemos una restricción para fijar el borde superior de cada espaciador al borde inferior del campo de texto sobre él. También crearemos una restricción para fijar el borde inferior de cada espaciador al borde superior del campo de texto debajo de él. Y finalmente, crearemos una restricción para forzar a cada espaciador a tener la misma altura que el espaciador superior.

Necesitaremos dos variables de instancia para configurar las cosas: una matriz de los campos de texto (en orden de arriba a abajo) y una referencia a la vista espaciadora superior:

@implementation ViewController {
    NSMutableArray *textFields;
    UIView *topSpacer;
}

Crearemos los campos de texto y los espaciadores en el código, ya que es difícil mostrar un xib en una respuesta de stackoverflow. Comenzamos cosas en viewDidLoad:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.translatesAutoresizingMaskIntoConstraints = NO;
    [self addTextFields];
    [self addSpacers];
}

Como vamos a usar el diseño automático, debemos desactivarlo translatesAutoresizingMaskIntoConstraints para evitar que el sistema cree restricciones adicionales.

Creamos cada campo de texto, le damos un texto ficticio y establecemos restricciones para su posición y tamaño horizontal:

- (void)addTextFields {
    textFields = [NSMutableArray array];
    for (int i = 0; i < 12; ++i) {
        [self addTextField];
    }
}

- (void)addTextField {
    UITextField *field = [[UITextField alloc] init];
    field.backgroundColor = [UIColor colorWithHue:0.8 saturation:0.1 brightness:0.9 alpha:1];
    field.translatesAutoresizingMaskIntoConstraints = NO;
    field.text = [field description];
    [self.view addSubview:field];
    [field setContentCompressionResistancePriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    [field setContentHuggingPriority:UILayoutPriorityRequired forAxis:UILayoutConstraintAxisVertical];
    [self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[field]-|" options:0 metrics:nil views:NSDictionaryOfVariableBindings(field)]];
    [textFields addObject:field];
}

Usaremos un bucle para crear los espaciadores también, pero creamos los espaciadores superior e inferior de manera diferente a los espaciadores intermedios, porque necesitamos fijar los espaciadores superior e inferior a la supervista:

- (void)addSpacers {
    [self addTopSpacer];
    for (int i = 1, count = textFields.count; i < count; ++i) {
        [self addSpacerFromBottomOfView:textFields[i - 1]
            toTopOfView:textFields[i]];
    }
    [self addBottomSpacer];
}

Así es como creamos el espaciador superior y configuramos sus restricciones. Su borde superior está fijado a la supervista y su borde inferior está anclado al primer campo de texto (el más alto). Almacenamos el espaciador superior en la variable de instancia topSpacerasí que podemos restringir a los otros espaciadores para que tengan la misma altura que el espaciador superior.

- (void)addTopSpacer {
    UIView *spacer = [self newSpacer];
    UITextField *field = textFields[0];
    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:|[spacer][field]" options:0 metrics:nil
        views:NSDictionaryOfVariableBindings(spacer, field)]];
    topSpacer = spacer;
}

Así es como realmente creamos una vista espaciadora. Es solo una vista oculta. Como no nos importa su tamaño o posición horizontal, simplemente lo fijamos a los bordes izquierdo y derecho de la supervista.

- (UIView *)newSpacer {
    UIView *spacer = [[UIView alloc] init];
    spacer.hidden = YES; // Views participate in layout even when hidden.
    spacer.translatesAutoresizingMaskIntoConstraints = NO;
    [self.view addSubview:spacer];
    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"|[spacer]|" options:0 metrics:nil
        views:NSDictionaryOfVariableBindings(spacer)]];
    return spacer;
}

Para crear un espaciador "medio" entre dos vistas de texto, lo fijamos al borde inferior del texto campo arriba y el borde superior del campo de texto a continuación. También limitamos su altura para igualar la altura del espaciador superior.

- (void)addSpacerFromBottomOfView:(UIView *)overView toTopOfView:(UIView *)underView {
    UIView *spacer = [self newSpacer];
    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:[overView][spacer(==topSpacer)][underView]" options:0 metrics:nil
        views:NSDictionaryOfVariableBindings(spacer, overView, underView, topSpacer)]];
}

Para crear el espaciador inferior, lo anclará en el último campo de texto y en la supervista. También limitamos su altura para igualar la altura del espaciador superior.

- (void)addBottomSpacer {
    UIView *spacer = [self newSpacer];
    UITextField *field = textFields.lastObject;
    [self.view addConstraints:[NSLayoutConstraint
        constraintsWithVisualFormat:@"V:[field][spacer(==topSpacer)]|" options:0 metrics:nil
        views:NSDictionaryOfVariableBindings(spacer, field, topSpacer)]];
}

Si lo haces bien, obtendrás un resultado como este:

example code screen shot

Puede encontrar un proyecto de ejemplo completo en este repositorio github.


49
2018-06-13 21:58



Revisa PureLayout. Está diseñado para ser la API más simple y más amigable con los programadores para crear restricciones de diseño automático en el código.

En respuesta a su pregunta específica, PureLayout ofrece dos API principales para distribuir vistas, una donde el espacio entre cada vista es fijo (el tamaño de la vista varía según sea necesario) y el otro donde se fija el tamaño de cada vista (el espacio entre las vistas varía según sea necesario). Este último logrará lo que estás buscando sin el uso de cualquier "punto de vista espaciador".

// NSArray+PureLayout.h

// ...

/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (variable) in the dimension along the axis and will have spacing (fixed) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
                                alignedTo:(ALAttribute)alignment
                         withFixedSpacing:(CGFloat)spacing;

/** Distributes the views in this array equally along the selected axis in their superview. Views will be the same size (fixed) in the dimension along the axis and will have spacing (variable) between them. */
- (NSArray *)autoDistributeViewsAlongAxis:(ALAxis)axis
                                alignedTo:(ALAttribute)alignment
                            withFixedSize:(CGFloat)size;

// ...

10
2017-08-19 05:32



ver la documentación de developer.apple ', que está teniendo una buena descripción de la solución, https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html ver el espaciado y deformación en esa página, creo que es una buena descripción, así que no hay necesidad de explicar lo mismo aquí

EDITAR

El enlace anterior ahora está deshabilitado por Apple. A partir de iOS 9, han presentado Stackview, que es la solución para todo esto.

Anteriormente en el enlace de arriba la respuesta era la misma que la respuesta proporcionada por @rob


2
2018-01-02 04:36