Равномерно распределить несколько видов в контейнере

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

Я создал демо-проект, чтобы попытаться найти помощь. Кто-нибудь знает, как сделать промежутки между представлениями увеличиваться или уменьшаться равномерно при изменении размера представления?

вот три метки (вручную разнесенные по вертикали):

image1

Я хочу, чтобы они изменили размер своего интервала (не размер вида) равномерно при вращении. По умолчанию верхний и Нижний виды хлюпают по направлению к центру:

image2

27 ответов


поэтому мой подход позволяет вам сделать это в interface builder. Что вы делаете, так это создаете "распорные виды", которые вы установили для соответствия высотам одинаково. Затем добавьте верхние и нижние ограничения к меткам (см. скриншот).

enter image description here

более конкретно, у меня есть верхнее ограничение на "Spacer View 1" для просмотра с ограничением высоты более низкого приоритета, чем 1000, и с высотой, равной всем другим "spacer views". 'Spacer View 4' имеет нижнее пространство ограничения к суперпанель. Каждая метка имеет соответствующие верхние и нижние ограничения для своих ближайших "видов прокладки".

Примечание: убедитесь, что у вас нет дополнительных ограничений по верхнему/нижнему пространству на ваших ярлыках для просмотра; только те, которые относятся к "пространственным представлениям". Это будет выполнима, поскольку верхнее и нижнее ограничения на пространство Вид 1 и вид прокладки 4' соответственно.

Duh 1: я дублировал свой вид и просто поместил его в ландшафтный режим, чтобы вы могли видеть, что он работал.

Duh 2: "spacer views" могли быть прозрачными.

Duh 3: Этот подход может применяться горизонтально.


СМОТРИТЕ, НЕТ РАСПОРКИ!

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

Я использую gifs, чтобы проиллюстрировать, насколько это просто. Надеюсь, вы найдете gifs полезными. На всякий случай, если у вас возникли проблемы с gifs, я включил старый ответ ниже с простыми снимками экрана.

инструкции:

1) добавить кнопки или ярлыки. Я использую 3 кнопки.

2) добавьте ограничение center x от каждой кнопки в superview:

enter image description here

3) добавьте ограничение от каждой кнопки к нижнему ограничению макета:

enter image description here

4) отрегулируйте ограничение, добавленное в #3 выше следующим образом:

a) выберите ограничение, b) удалить константу (значение 0), c) измените множитель следующим образом: возьмите количество кнопок + 1 и, начиная сверху, установите множитель как buttonCountPlus1: 1, а потом buttonCountPlus1: 2 и наконец buttonCountPlus1: 3. (Я объясняю, откуда я взял эту формулу в старом ответе ниже, если вы заинтересованный.)

enter image description here

5) вот демо работает!

enter image description here

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


Ответ


несмотря на то, что документы Apple и отличная книга Эрики Садун (Автоматическая Разметка Демистифицирована) говорят, это возможно равномерное пространство просмотров без проставки. Это очень просто сделать в IB и в код для любого количества элементов, которые нужно равномерно. Все, что вам нужно, это математическая формула, называемая "формула раздела". Это проще сделать, чем объяснить. Я постараюсь продемонстрировать это в IB, но это так же легко сделать в коде.

в рассматриваемом примере вы бы

1) Начните с установки каждой метки для ограничения центра. Это очень просто делать. Просто управляйте перетаскиванием от каждой метки вниз.

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

3) Выберите "нижнее пространство для нижней направляющей компоновки "и"центр по горизонтали в контейнере". Сделайте это для всех 3 меток.

Hold down shift to add these two constraints for each label

в принципе, если мы возьмем метку, координату которой мы хотим определить, и разделим ее на общее число меток плюс 1, тогда у нас есть число, которое мы можем добавить в IB, чтобы получить динамическое местоположение. Я упрощаю формулу, но вы можете использовать ее для установки горизонтального расстояния или вертикального и горизонтального одновременно. Это супер мощный!

вот наши мультипликаторы.

Label1 = 1/4 = .25,

Label2 = 2/4 = .5,

Label3 = 3/4 = .75

(Edit: @Rivera прокомментировал, что вы можете просто использовать коэффициенты непосредственно в множителе поле и xCode с математикой!)

4) Итак, давайте выберем Label1 и выберем нижнее ограничение. Вроде этого: enter image description here

5) выберите" второй элемент " в Инспекторе атрибутов.

6) в раскрывающемся списке выберите "обратный первый и второй элемент".

7) обнулить константу и значение wC hAny. (Вы можете добавить смещение здесь, Если вам это нужно).

8) это критическая часть: в поле множителя добавьте наш первый множитель 0.25.

9) в то время как вы на нем установите верхний "первый элемент" в "CenterY", так как мы хотим центрировать его в y-центр метки. Вот как все это должно выглядеть.

enter image description here

10) повторите этот процесс для каждой этикетки и подключите соответствующий множитель: 0.5 для Label2 и 0.75 для Label3. Вот конечный продукт во всех ориентациях со всеми компактными устройствами! Очень просто. Я рассматривал много решений, связанных с пачками кода, и распорки. Это далеко не лучшее решение, которое я видел по этому вопросу.

Update: @kraftydevil добавляет, что нижнее руководство по компоновке отображается только в раскадровках, а не в xibs. Использовать нижнее пространство для контейнера в xibs. Хороший улов!

enter image description here


очень быстрый интерфейс Builder решение:

для любого количества представлений, равномерно расположенных в пределах супервизора, просто дайте каждому ограничение" выровнять центр X для супервизора "для горизонтальной компоновки или" выровнять центр y супервизор " для вертикальной компоновки и установите множитель N:p (Примечание: некоторым повезло больше с p:N см. ниже)

здесь

N = total number of views и

p = position of the view including spaces

первая позиция равна 1, затем пробел, делая следующую позицию 3, поэтому p становится серией [1,3,5,7,9,...]. Работает для любого количества просмотров.

Итак, если у вас есть 3 видом в космос, это выглядит так:

Illustration of how to evenly spread views in IB

редактировать Примечание: выбор N:p или p:N зависит от порядка отношений вашего ограничения выравнивания. Если" первый элемент " - Superview.Центр, вы можете использовать p:N, в то время как если Superview.Центр - " второй Пункт", вы можете использовать N:p. Если сомневаетесь, попробуйте оба варианта... :-)


начиная с iOS 9, Apple сделала это очень легко с (долгожданным) UIStackView. Просто выберите представления, которые вы хотите содержать в Построителе интерфейса, и выберите редактор - > встроить в - > вид стека. Установите соответствующие ограничения ширины / высоты / поля для представления стека и убедитесь, что свойство Distribution имеет значение "равный интервал":

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


Я был на американских горках любви autolayout и ненавижу его. Ключом к любви, по-видимому, является принятие следующего:

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

тем не менее, то, что вы пытаетесь не просто и было бы трудно достичь в interface builder. Это довольно просто сделать в коде. Этот код, в viewDidLoad, создает и позиционирует три метки, как вы их просите:

// Create three labels, turning off the default constraints applied to views created in code
UILabel *label1 = [UILabel new];
label1.translatesAutoresizingMaskIntoConstraints = NO;
label1.text = @"Label 1";

UILabel *label2 = [UILabel new];
label2.translatesAutoresizingMaskIntoConstraints = NO;
label2.text = @"Label 2";

UILabel *label3 = [UILabel new];
label3.translatesAutoresizingMaskIntoConstraints = NO;
label3.text = @"Label 3";

// Add them all to the view
[self.view addSubview:label1];
[self.view addSubview:label2];
[self.view addSubview:label3];

// Center them all horizontally
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];

// Center the middle one vertically
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label2 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self.view attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0]];

// Position the top one half way up
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label1 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label2 attribute:NSLayoutAttributeCenterY multiplier:0.5 constant:0]];

// Position the bottom one half way down
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:label3 attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:label2 attribute:NSLayoutAttributeCenterY multiplier:1.5 constant:0]];

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

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


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

пример использования для вертикального распределения 4 меток в пределах их superview:

[self.view addConstraints:
     [NSLayoutConstraint constraintsForEvenDistributionOfItems:@[label1, label2, label3, label4]
                                        relativeToCenterOfItem:self.view
                                                    vertically:YES]];

NSLayoutConstraint+EvenDistribution.h

@interface NSLayoutConstraint (EvenDistribution)

/**
 * Returns constraints that will cause a set of views to be evenly distributed horizontally
 * or vertically relative to the center of another item. This is used to maintain an even
 * distribution of subviews even when the superview is resized.
 */
+ (NSArray *) constraintsForEvenDistributionOfItems:(NSArray *)views
                             relativeToCenterOfItem:(id)toView
                                         vertically:(BOOL)vertically;

@end

NSLayoutConstraint+EvenDistribution.м

@implementation NSLayoutConstraint (EvenDistribution)

+(NSArray *)constraintsForEvenDistributionOfItems:(NSArray *)views
                           relativeToCenterOfItem:(id)toView vertically:(BOOL)vertically
{
    NSMutableArray *constraints = [NSMutableArray new];
    NSLayoutAttribute attr = vertically ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX;

    for (NSUInteger i = 0; i < [views count]; i++) {
        id view = views[i];
        CGFloat multiplier = (2*i + 2) / (CGFloat)([views count] + 1);
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
                                                                      attribute:attr
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:toView
                                                                      attribute:attr
                                                                     multiplier:multiplier
                                                                       constant:0];
        [constraints addObject:constraint];
    }

    return constraints;
}

@end

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

С NSArray + PureLayout.h:

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

// ...

Так как это все с открытым исходным кодом, если вам интересно увидеть, как это достигается без пространственных представлений, просто взгляните на реализацию. (Это зависит от использования обоих constant и multiplier для ограничений.)


правильный и простой способ-использовать представления стека.

  1. добавить метки/вид Посмотреть Стек:

enter image description here

  1. выберите Вид стека и установите распределение на Равный Интервал:

enter image description here

  1. добавить расстояние до ближайшего соседа ограничения на представление стека и обновление кадров:

enter image description here

  1. добавить Высота ограничения для всех меток:

enter image description here

  1. Наслаждайтесь Просмотром:

enter image description here


У меня аналогичная проблема и обнаружил этот пост. Однако ни один из представленных в настоящее время ответов не решает проблему так, как вы хотите. Они не делают интервал равным, а скорее распределяют центр меток поровну. Важно понимать, что это не одно и то же. Я построил небольшую диаграмму, чтобы проиллюстрировать это.

View Spacing Illustration

есть 3 вида, все 20pt высотой. Использование любого из предложенных методов равномерно распределяет центры видов и дают вам иллюстрированный макет. Обратите внимание, что y-центр представлений разнесен поровну. Однако расстояние между супервизором и видом сверху составляет 15pt, а расстояние между подвидами-всего 5pt. Чтобы виды были разнесены одинаково, они должны быть 10pt, т. е. все синие стрелки должны быть 10pt.

тем не менее, я еще не придумал хорошее общее решение. В настоящее время моя лучшая идея-вставить "дистанционирование" между подвидами и установить высоты представлений интервалов должны быть равными.


Я смог решить это полностью В IB:

  1. сделайте ограничения для выравнивания центра Y каждого из ваших подвидов к нижнему краю супервизора.
  2. установите множитель каждого из этих ограничений в 1 / 2n, 3/2n, 5/2n,..., n-1/2n, где n-количество подвидов, которые вы распространяете.

поэтому, если у вас есть три метки, установите множители для каждого из этих ограничений в 0.1666667, 0.5, 0.833333.


Я нашел идеальный и простой метод. Автоматическая компоновка не позволяет изменять размер пространств поровну, но позволяет изменять размер представлений поровну. Просто поместите некоторые невидимые виды между вашими полями и скажите auto layout, чтобы сохранить их одинаковый размер. Он работает отлично!

Initial XIB

Stretched XIB

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

edit: я обнаружил, что конфликт стал проблемой. Из-за этого я взял одно из ограничений интервала, удалил его и заменил двумя ограничениями, большим или равным и меньшим или равным. Оба они были одинакового размера и имели гораздо более низкий приоритет, чем другие ограничения. В результате конфликт прекратился.


проверить https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/AutoLayoutbyExample/AutoLayoutbyExample.html имея хорошее описание о решении вашей проблемы.


с метками это работает нормально, по крайней мере:

@"H:|-15-[first(==second)]-[second(==third)]-[third(==first)]-15-|

Если первый имеет ту же ширину, что и второй, и второй третий, и третий первый, то все они получат ту же ширину... Вы можете сделать это как по горизонтали (H), так и по вертикали (V).


основываясь на ответе Бена Долмана, это распределяет представления более равномерно (с заполнением и т. д.):

+(NSArray *)constraintsForEvenDistributionOfItems:(NSArray *)views
                           relativeToCenterOfItem:(id)toView vertically:(BOOL)vertically
{
    NSMutableArray *constraints = [NSMutableArray new];
    NSLayoutAttribute attr = vertically ? NSLayoutAttributeCenterY : NSLayoutAttributeCenterX;

    CGFloat min = 0.25;
    CGFloat max = 1.75;
    CGFloat d = (max-min) / ([views count] - 1);
    for (NSUInteger i = 0; i < [views count]; i++) {
        id view = views[i];
        CGFloat multiplier = i * d + min;
        NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:view
                                                                      attribute:attr
                                                                      relatedBy:NSLayoutRelationEqual
                                                                         toItem:toView
                                                                      attribute:attr
                                                                     multiplier:multiplier
                                                                       constant:0];
        [constraints addObject:constraint];
    }

    return constraints;
}

Я знаю, что прошло некоторое время с первого ответа, но я только что столкнулся с той же проблемой, и я хочу поделиться своим решением. Для грядущих поколений...

Я устанавливаю свои взгляды на viewDidLoad:

- (void)viewDidLoad {

    [super viewDidLoad];

    cancelButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    cancelButton.translatesAutoresizingMaskIntoConstraints = NO;
    [cancelButton setTitle:@"Cancel" forState:UIControlStateNormal];
    [self.view addSubview:cancelButton];

    middleButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    middleButton.translatesAutoresizingMaskIntoConstraints = NO;
    [middleButton setTitle:@"Middle" forState:UIControlStateNormal];
    [self.view addSubview:middleButton];

    nextButton = [UIButton buttonWithType: UIButtonTypeRoundedRect];
    nextButton.translatesAutoresizingMaskIntoConstraints = NO;
    [nextButton setTitle:@"Next" forState:UIControlStateNormal];
    [self.view addSubview:nextButton];


    [self.view setNeedsUpdateConstraints];

}

и затем, в updateViewConstrains, сначала я удаляю все ограничения, затем создаю словарь представлений, а затем вычисляю пространство, которое будет использоваться между представлениями. После этого я просто использую формат Visual Language для установки ограничения:

- (void)updateViewConstraints {


    [super updateViewConstraints];

    [self.view removeConstraints:self.view.constraints];

    NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(cancelButton, nextButton, middleButton);

    float distance=(self.view.bounds.size.width-cancelButton.intrinsicContentSize.width-nextButton.intrinsicContentSize.width-middleButton.intrinsicContentSize.width-20-20)/  ([viewsDictionary count]-1);  // 2 times 20 counts for the left & rigth margins
    NSNumber *distancies=[NSNumber numberWithFloat:distance];

//    NSLog(@"Distancies: %@", distancies);
//    
//    NSLog(@"View Width: %f", self.view.bounds.size.width);
//    NSLog(@"Cancel Width: %f", cancelButton.intrinsicContentSize.width);
//    NSLog(@"Middle Width: %f", middleButton.intrinsicContentSize.width);
//    NSLog(@"Next Width: %f", nextButton.intrinsicContentSize.width);



    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|-[cancelButton]-dis-[middleButton]-dis-[nextButton]-|"
                                                                   options:NSLayoutFormatAlignAllBaseline
                                                                   metrics:@{@"dis":distancies}
                                                                     views:viewsDictionary];


    [self.view addConstraints:constraints];



    constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:[nextButton]-|"
                                                          options:0
                                                          metrics:nil
                                                            views:viewsDictionary];
    [self.view addConstraints:constraints];



}

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

надеюсь, это поможет.


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

в этом примере self - это супервизор, в котором вы центрируете все подвиды.

NSArray *subviews = @[ (your subviews in top-to-bottom order) ];

UIView *container = [[UIView alloc] initWithFrame:CGRectZero];
container.translatesAutoresizingMaskIntoConstraints = NO;
for (UIView *subview in subviews) {
    subview.translatesAutoresizingMaskIntoConstraints = NO;
    [container addSubview:subview];
}
[self addSubview:container];

[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeLeft relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeLeft multiplier:1.0f constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeRight relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeRight multiplier:1.0f constant:0.0f]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:container attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual
                                                    toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0f constant:0.0f]];

if (0 < subviews.count) {
    UIView *firstSubview = subviews[0];
    [container addConstraint:[NSLayoutConstraint constraintWithItem:firstSubview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                             toItem:container attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]];
    UIView *lastSubview = subviews.lastObject;
    [container addConstraint:[NSLayoutConstraint constraintWithItem:lastSubview attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual
                                                             toItem:container attribute:NSLayoutAttributeBottom multiplier:1.0f constant:0.0f]];

    UIView *subviewAbove = nil;
    for (UIView *subview in subviews) {
        [container addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual
                                                                 toItem:container attribute:NSLayoutAttributeCenterX multiplier:1.0f constant:0.0f]];
        if (subviewAbove) {
            [container addConstraint:[NSLayoutConstraint constraintWithItem:subview attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual
                                                                     toItem:subviewAbove attribute:NSLayoutAttributeBottom multiplier:1.0f constant:10.0f]];
        }
        subviewAbove = subview;
    }
}

Я только что решил свою проблему, используя функцию множителя. Я не уверен, что это работает для всех случаев, но для меня это сработало отлично. Я на Xcode 6.3 FYI.

в итоге я сделал следующее:

1) Сначала мои кнопки расположены на экране ширины 320px, распределенном так, как я хотел, чтобы он выглядел на устройстве 320px.

step 1: getting buttons positioned

2) Затем я добавил ведущее ограничение пространства для просмотра на всех моих кнопки.

step 2: add leading space constraints

3) Затем я изменил свойства ведущего пространства так, чтобы константа была 0, а множитель-смещение x, деленное на ширину экрана (например, моя первая кнопка была 8px от левого края, поэтому я установил множитель на 8/320)

4) тогда важным шагом здесь является изменение второго элемента в отношении ограничения, чтобы быть супервизором.Трейлинг вместо superview.ведущий. Это ключ, потому что superview.Ведущий-0 и трейлинг в моем случае 320, поэтому 8/320-8 px на устройстве 320px, а затем, когда ширина супервизора изменяется на 640 или что-то еще, все виды перемещаются в соотношении к ширине размера экрана 320px. Математика здесь гораздо проще для понимания.

step 3 & 4: change multiplier to xPos/screenWidth and set second item to .Trailing


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

NSDictionary *views = NSDictionaryOfVariableBindings(_redView, _yellowView, _blueView);

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|->=0-[_redView(40)]->=0-[_yellowView(40)]->=0-[_blueView(40)]->=0-|" options:NSLayoutFormatAlignAllTop | NSLayoutFormatAlignAllBottom metrics:nil views:views]];

[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[_redView(60)]" options:0 metrics:nil views:views]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:_redView attribute:NSLayoutAttributeCenterY multiplier:1 constant:0]];

[self.view addConstraint:[NSLayoutConstraint constraintWithItem:self.view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeCenterX multiplier:1 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_redView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeLeading multiplier:0.5 constant:0]];
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:_blueView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:_yellowView attribute:NSLayoutAttributeLeading multiplier:1.5 constant:40]];

вот еще один ответ. Я отвечал на аналогичный вопрос и видел ссылку, связанную с этим вопросом. Я не видел ответа, похожего на мой. Поэтому я решил написать ее здесь.

  class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.whiteColor()
        setupViews()
    }

    var constraints: [NSLayoutConstraint] = []

    func setupViews() {

        let container1 = createButtonContainer(withButtonTitle: "Button 1")
        let container2 = createButtonContainer(withButtonTitle: "Button 2")
        let container3 = createButtonContainer(withButtonTitle: "Button 3")
        let container4 = createButtonContainer(withButtonTitle: "Button 4")

        view.addSubview(container1)
        view.addSubview(container2)
        view.addSubview(container3)
        view.addSubview(container4)

        [

            // left right alignment
            container1.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 20),
            container1.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: -20),
            container2.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container2.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
            container3.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container3.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),
            container4.leftAnchor.constraintEqualToAnchor(container1.leftAnchor),
            container4.rightAnchor.constraintEqualToAnchor(container1.rightAnchor),


            // place containers one after another vertically
            container1.topAnchor.constraintEqualToAnchor(view.topAnchor),
            container2.topAnchor.constraintEqualToAnchor(container1.bottomAnchor),
            container3.topAnchor.constraintEqualToAnchor(container2.bottomAnchor),
            container4.topAnchor.constraintEqualToAnchor(container3.bottomAnchor),
            container4.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),


            // container height constraints
            container2.heightAnchor.constraintEqualToAnchor(container1.heightAnchor),
            container3.heightAnchor.constraintEqualToAnchor(container1.heightAnchor),
            container4.heightAnchor.constraintEqualToAnchor(container1.heightAnchor)
            ]
            .forEach { .active = true }
    }


    func createButtonContainer(withButtonTitle title: String) -> UIView {
        let view = UIView(frame: .zero)
        view.translatesAutoresizingMaskIntoConstraints = false

        let button = UIButton(type: .System)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.setTitle(title, forState: .Normal)
        view.addSubview(button)

        [button.centerYAnchor.constraintEqualToAnchor(view.centerYAnchor),
            button.leftAnchor.constraintEqualToAnchor(view.leftAnchor),
            button.rightAnchor.constraintEqualToAnchor(view.rightAnchor)].forEach { .active = true }

        return view
    }
}

enter image description here

и опять же, это можно сделать довольно легко с помощью iOS9 UIStackViews.

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = UIColor.greenColor()
        setupViews()
    }

    var constraints: [NSLayoutConstraint] = []

    func setupViews() {

        let container1 = createButtonContainer(withButtonTitle: "Button 1")
        let container2 = createButtonContainer(withButtonTitle: "Button 2")
        let container3 = createButtonContainer(withButtonTitle: "Button 3")
        let container4 = createButtonContainer(withButtonTitle: "Button 4")

        let stackView = UIStackView(arrangedSubviews: [container1, container2, container3, container4])
        stackView.translatesAutoresizingMaskIntoConstraints = false
        stackView.axis = .Vertical
        stackView.distribution = .FillEqually
        view.addSubview(stackView)

        [stackView.topAnchor.constraintEqualToAnchor(view.topAnchor),
            stackView.bottomAnchor.constraintEqualToAnchor(view.bottomAnchor),
            stackView.leftAnchor.constraintEqualToAnchor(view.leftAnchor, constant: 20),
            stackView.rightAnchor.constraintEqualToAnchor(view.rightAnchor, constant: -20)].forEach { .active = true }
    }


    func createButtonContainer(withButtonTitle title: String) -> UIView {
        let button = UIButton(type: .Custom)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.backgroundColor = UIColor.redColor()
        button.setTitleColor(UIColor.whiteColor(), forState: .Normal)
        button.setTitle(title, forState: .Normal)
        let buttonContainer = UIStackView(arrangedSubviews: [button])
        buttonContainer.distribution = .EqualCentering
        buttonContainer.alignment = .Center
        buttonContainer.translatesAutoresizingMaskIntoConstraints = false
        return buttonContainer
    }
}

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


swift 3 версия

let _redView = UIView()
        _redView.backgroundColor = UIColor.red
        _redView.translatesAutoresizingMaskIntoConstraints = false

        let _yellowView = UIView()
        _yellowView.backgroundColor = UIColor.yellow
        _yellowView.translatesAutoresizingMaskIntoConstraints = false

        let _blueView = UIView()
        _blueView.backgroundColor = UIColor.blue
        _blueView.translatesAutoresizingMaskIntoConstraints = false

        self.view.addSubview(_redView)
        self.view.addSubview(_yellowView)
        self.view.addSubview(_blueView)

        var views = ["_redView": _redView, "_yellowView": _yellowView, "_blueView":_blueView]

        var nslayoutConstraint_H = NSLayoutConstraint.constraints(withVisualFormat: "|->=0-[_redView(40)]->=0-[_yellowView(40)]->=0-[_blueView(40)]->=0-|", options: [.alignAllTop, .alignAllBottom], metrics: nil, views: views)
        self.view.addConstraints(nslayoutConstraint_H)

        var nslayoutConstraint_V = NSLayoutConstraint.constraints(withVisualFormat: "V:[_redView(60)]", options: NSLayoutFormatOptions.init(rawValue: 0), metrics: nil, views: views)
        self.view.addConstraints(nslayoutConstraint_V)


        let constraint_red = NSLayoutConstraint.init(item: self.view, attribute: .centerY, relatedBy: .equal, toItem: _redView, attribute: .centerY, multiplier: 1, constant: 0)
        self.view.addConstraint(constraint_red)

        let constraint_yellow = NSLayoutConstraint.init(item: self.view, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .centerX, multiplier: 1, constant: 0)
        self.view.addConstraint(constraint_yellow)

        let constraint_yellow1 = NSLayoutConstraint.init(item: _redView, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .leading, multiplier: 0.5, constant: 0)
        self.view.addConstraint(constraint_yellow1)

        let constraint_yellow2 = NSLayoutConstraint.init(item: _blueView, attribute: .centerX, relatedBy: .equal, toItem: _yellowView, attribute: .leading, multiplier: 1.5, constant: 40)
        self.view.addConstraint(constraint_yellow2)

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

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

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


очень простой способ решить эту проблему в InterfaceBuilder:

установите центрированную метку (label2) в "горизонтальный центр в контейнере" и "вертикальный центр в контейнере"

выберите центрированную метку и верхнюю метку (label1 + label2) и добавьте два ограничения по вертикали. один С больше или равно мин. расстояние. один С меньше или равно максимальное расстояние.

в то же самое для центрированной метки и нижней метки (label2 + label3).

кроме того, вы также можете добавить два ограничения для label1-Top Space для просмотра и два ограничения для label2-Bottom Space для просмотра.

результат будет, что все 4 расстояния будут одинаково изменять их размеры.


Я сделал функцию, которая может помочь. Этот пример использования :

 [self.view addConstraints: [NSLayoutConstraint fluidConstraintWithItems:NSDictionaryOfVariableBindings(button1, button2, button3)
                                                                asString:@[@"button1", @"button2", @"button3"]
                                                               alignAxis:@"V"
                                                          verticalMargin:100
                                                        horizontalMargin:50
                                                             innerMargin:25]];

приведет к тому, что вертикальное распределение (извините, у вас нет репутации 10 для вставки изображений). И если вы измените ось и некоторые значения маржи:

alignAxis:@"H"
verticalMargin:120
horizontalMargin:20
innerMargin:10

ты горизонтальное распределение.

Я новичок в iOS, но вуаля !

EvenDistribution.h

@interface NSLayoutConstraint (EvenDistribution)

/**
 * Returns constraints that will cause a set of subviews
 * to be evenly distributed along an axis.
 */
+ (NSArray *)  fluidConstraintWithItems:(NSDictionary *) views
                               asString:(NSArray *) stringViews
                              alignAxis:(NSString *) axis
                         verticalMargin:(NSUInteger) vMargin
                       horizontalMargin:(NSUInteger) hMargin
                            innerMargin:(NSUInteger) inner;
@end

EvenDistribution.м

#import "EvenDistribution.h"

@implementation NSLayoutConstraint (EvenDistribution)

+ (NSArray *) fluidConstraintWithItems:(NSDictionary *) dictViews
                              asString:(NSArray *) stringViews
                             alignAxis:(NSString *) axis
                        verticalMargin:(NSUInteger) vMargin
                      horizontalMargin:(NSUInteger) hMargin
                           innerMargin:(NSUInteger) iMargin

{
    NSMutableArray *constraints = [NSMutableArray arrayWithCapacity: dictViews.count];
    NSMutableString *globalFormat = [NSMutableString stringWithFormat:@"%@:|-%d-",
                                     axis,
                                     [axis isEqualToString:@"V"] ? vMargin : hMargin
                                     ];



        for (NSUInteger i = 0; i < dictViews.count; i++) {

            if (i == 0)
                [globalFormat appendString:[NSString stringWithFormat: @"[%@]-%d-", stringViews[i], iMargin]];
            else if(i == dictViews.count - 1)
                [globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-", stringViews[i], stringViews[i-1]]];
            else
               [globalFormat appendString:[NSString stringWithFormat: @"[%@(==%@)]-%d-", stringViews[i], stringViews[i-1], iMargin]];

            NSString *localFormat = [NSString stringWithFormat: @"%@:|-%d-[%@]-%d-|",
                                     [axis isEqualToString:@"V"] ? @"H" : @"V",
                                     [axis isEqualToString:@"V"] ? hMargin : vMargin,
                                     stringViews[i],
                                     [axis isEqualToString:@"V"] ? hMargin : vMargin];

            [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:localFormat
                                                                                     options:0
                                                                                     metrics:nil
                                                                                       views:dictViews]];


    }
    [globalFormat appendString:[NSString stringWithFormat:@"%d-|",
                                [axis isEqualToString:@"V"] ? vMargin : hMargin
                                ]];

    [constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:globalFormat
                                                                             options:0
                                                                             metrics:nil
                                                                               views:dictViews]];

    return constraints;

}

@end

Да, вы можете сделать это исключительно в interface builder и без написания кода - одно предостережение заключается в том, что вы изменяете размер метки вместо распространения пробелов. В этом случае выровняйте метку 2 X и Y с супервизором, чтобы она была закреплена в центре. Затем установите вертикальное пространство метки 1 в супервизор и метку 2 в стандарт, повторите для метки 3. После установки метки 2 Самый простой способ установить метки 1 и 3-изменить их размер, пока они не защелкнутся.

вот горизонтальная дисплей, обратите внимание, что вертикальное пространство между метками 1 и 2 установлено в стандарт:horizontal display

и вот портретная версия:enter image description here

Я понимаю, что они не совсем 100% одинаково расположены между базовыми линиями из-за разницы между стандартным пространством между метками и стандартным пространством для супервизора. Если это беспокоит вас, установите размер в 0 вместо стандартного


Я устанавливаю значение ширины только для первого элемента (>=ширина) и минимальное расстояние между каждым элементом (>=расстояние). Затем я использую Ctrl для перетаскивания второго, третьего... item on the first для цепочки зависимостей между элементами.

enter image description here


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

const float MENU_HEIGHT = 40;

- (UIView*) createMenuWithLabels: (NSArray *) labels
    // labels is NSArray of NSString
    UIView * backgroundView = [[UIView alloc]init];
    backgroundView.translatesAutoresizingMaskIntoConstraints = false;

    NSMutableDictionary * views = [[NSMutableDictionary alloc] init];
    NSMutableString * format = [[NSMutableString alloc] initWithString: @"H:|"];
    NSString * firstLabelKey;

    for(NSString * str in labels)
    {
        UILabel * label = [[UILabel alloc] init];
        label.translatesAutoresizingMaskIntoConstraints = false;
        label.text = str;
        label.textAlignment = NSTextAlignmentCenter;
        label.textColor = [UIColor whiteColor];
        [backgroundView addSubview: label];
        [label fixHeightToTopBounds: MENU_HEIGHT-2];
        [backgroundView addConstraints: [label fixHeightToTopBounds: MENU_HEIGHT]];
        NSString * key = [self camelCaseFromString: str];
        [views setObject: label forKey: key];
        if(firstLabelKey == nil)
        {
            [format appendString: [NSString stringWithFormat: @"[%@]", key]];
            firstLabelKey = key;
        }
        else
        {
            [format appendString: [NSString stringWithFormat: @"[%@(==%@)]", key, firstLabelKey]];
        }
    }

    [format appendString: @"|"];

    NSArray * constraints = [NSLayoutConstraint constraintsWithVisualFormat: (NSString *) format
                                                                               options: 0
                                                                               metrics: nil
                                                                                 views: (NSDictionary *) views];
    [backgroundView addConstraints: constraints];
    return backgroundView;
}

почему бы вам просто не создать tableView и сделать isScrollEnabled = false