iOS Autolayout вертикально равное пространство для заполнения родительского вида

у меня есть контроллер вида с 12 UITextFields.

Он очень хорошо подходит для 3,5" дисплей.

enter image description here

мне нужно установить его для iPhone 5 (4-дюймовый дисплей), чтобы все UITextFields покрывают все UIView путем добавления дополнительного пространства между ними.

Я пытаюсь сделать это с помощью автоматического макета, но он не работает должным образом.

enter image description here

Это мой код :

- (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];
}

если я делаю [txt12(==txt1)] затем он отображает то же, что и Экран 3.5" и оставляет пространство ниже.

где я делаю ошибку?

3 ответов


чтобы сделать это с помощью auto layout, необходимо создать дополнительные представления для заполнения пробелов между текстовыми полями.

Напомним, что ограничение автоматической компоновки в основном является линейным уравнением A = m * B + c. A является атрибутом одного вида (например, координата Y viewA's нижняя кромка) и B является атрибутом другого представления (например, координата Y viewBверхний край). m и c константы. Так, например, выложить viewA и viewB так что есть 30 точек между нижней частью viewA и верхней viewB, мы могли бы создать ограничение, где m 1 и c - это -30.

проблема в том, что вы хотите использовать одно и то же значение для c через 13 различных ограничений, и вы хотите, чтобы автоматический макет вычислял это c значение для вас. Auto layout просто не может этого сделать. Не напрямую. Автоматический макет может вычислять только атрибуты представлений; он не может вычислить m и c константы.

есть способ сделать автоматический макет, чтобы поместить представления, где вы хотите: reify пространства между текстовыми полями как дополнительные (невидимые) представления. Вот пример всего с 3 текстовыми полями:

example layout

мы создадим ограничение, чтобы прикрепить верхний край каждой прокладки к нижнему краю текстового поля над ним. Мы также создадим ограничение, чтобы прикрепить нижний край каждой прокладки к верхнему краю текста полем ниже. И, наконец, мы создадим ограничение, чтобы заставить каждую прокладку иметь ту же высоту, что и самая верхняя прокладка.

нам понадобятся две переменные экземпляра для настройки: массив текстовых полей (в порядке сверху вниз) и ссылка на самый верхний вид прокладки:

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

мы создадим текстовые поля и прокладки в коде, так как трудно показать xib в ответе stackoverflow. Мы пнуть вещи в viewDidLoad:

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

так как мы собираемся использовать Auto layout, нам нужно отключить translatesAutoresizingMaskIntoConstraints чтобы предотвратить создание дополнительных ограничений.

мы создаем каждое текстовое поле, даем ему фиктивный текст и устанавливаем ограничения для его горизонтального положения и размера:

- (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];
}

мы будем использовать петлю для создания прокладок тоже, но мы создаем верхние и нижние прокладки по-разному от средних прокладок, потому что нам нужно прикрепить верхние и нижние прокладки к в суперпанель:

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

вот как мы создаем верхнюю прокладку и устанавливаем ее ограничения. Его верхний край прикреплен к супервизору, а нижний край прикреплен к первому (самому верхнему) текстовому полю. Мы храним верхнюю прокладку в переменной экземпляра topSpacer таким образом, мы можем ограничить другие прокладки, чтобы иметь ту же высоту, что и верхняя прокладка.

- (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;
}

вот как мы на самом деле создаем вид пространства. Это просто скрытый вид. Поскольку нас не волнует его горизонтальность размер или положение, мы просто прикрепляем его к левому и правому краям супервизора.

- (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;
}

чтобы создать" среднюю " прокладку между двумя текстовыми представлениями, мы прикрепляем ее к нижнему краю текста поле вверху и верхний край текстового поля внизу. Мы также ограничиваем его высоту, чтобы равняться высоте верхней прокладки.

- (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)]];
}

чтобы создать нижнюю прокладку, мы прикрепляем ее к последнему текстовому полю и к супервизору. Мы также ограничиваем его высоту, чтобы равняться высоте вершины распорка.

- (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)]];
}

если вы сделаете это правильно, вы получите такой результат:

example code screen shot

вы можете найти полный пример проекта в этот репозиторий github.


проверить PureLayout. Он разработан как самый простой и удобный для программиста API для создания ограничений автоматической компоновки в коде.

в ответ на ваш конкретный вопрос PureLayout предлагает два основных API для распространения представлений: один, где расстояние между каждым представлением фиксировано (размер представления изменяется по мере необходимости), а другой, где размер каждого представления фиксирован (расстояние между представлениями изменяется по мере необходимости). Последний выполнит то, что вы ищу без использование любых "видов распорки".

// 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;

// ...

см. раздел разработчик.документация apple, которая имеет хорошее описание решения, https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html см. интервал и деформацию на этой странице, Я думаю, что это хорошее описание, поэтому не нужно объяснять то же самое здесь

редактировать

выше ссылка теперь отключена apple, так как с iOS 9 у них есть введен Stackview, который является решением для всего этого.

ранее в приведенной выше ссылке ответ был таким же, как ответ, предоставленный @rob