Подтвердить что ты не робот

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

У меня есть контроллер вида с 12 UITextField s.

Он хорошо подходит для 3,5-дюймового дисплея.

enter image description here

Мне нужно установить его для iPhone 5 (4-дюймовый дисплей), чтобы весь UITextField охватывал весь 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-дюймовый экран, и оставляет пространство ниже.

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

4b9b3361

Ответ 1

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

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

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

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

пример макета

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

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

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

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

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

Поскольку мы собираемся использовать автоматическую компоновку, нам нужно отключить 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)]];
}

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

примерный снимок экрана кода

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

Ответ 2

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

В ответ на ваш конкретный вопрос 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;

// ...

Ответ 3

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

ИЗМЕНИТЬ

Выше ссылка теперь отключена яблоком. Как и в iOS 9, они внедрили Stackview, что является решением для всего этого.

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