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

279

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

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

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

image1

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

image2

nothappybob
источник
может быть полезно установить ограничение от одной метки к другой и установить для них отношение «больше или равно»
BergP
программно, например, с использованием этого метода NSLayoutConstraint: + (id) constraintWithItem: (id) атрибут view1: (NSLayoutAttribute) attr1 relatedBy: (NSLayoutRelation) отношение к toItem: (id) атрибут view2: (NSLayoutAttribute) множитель attr2: (множитель CGFloat) константа: (CGFloat) c
BergP
Я открываю исходную замену универсальной замены UITabBar, которая обобщает этот подход с использованием Auto Layout. Вы можете проверить тесты, чтобы увидеть, как я генерирую свои ограничения Auto Layout. GGTabBar
Goles

Ответы:

211

Так что мой подход позволяет вам сделать это в конструкторе интерфейсов. То, что вы делаете, это создаете «разделительные виды», которые вы установили так, чтобы они одинаково соответствовали высотам. Затем добавьте верхние и нижние ограничения к меткам (см. Скриншот).

введите описание изображения здесь

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

Примечание: убедитесь, что у вас нет дополнительных верхних / нижних пространственных ограничений на ваших ярлыках для супервизора; только те, которые относятся к «космическим видам». Это будет удовлетворительным, так как верхнее и нижнее ограничения находятся в «Space View 1» и «Spacer View 4» соответственно.

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

Дух 2: «Вид спейсера» мог бы быть прозрачным.

Дух 3: Этот подход может быть применен горизонтально.

Спенсер Холл
источник
10
Это работает. Фактически, спейсерные виды могут быть скрыты, и они все равно будут создавать правильную компоновку.
paulmelnikow
Это было супер полезно. Как и комментарий о том, чтобы просто пометить проставки как скрытые. Тогда вы все еще можете видеть их в вашей раскадровке / XIB, но они не отображаются в вашем приложении. Очень хорошо.
shulmey
Я получил почти работать после нескольких часов повторных попыток. К сожалению, XCode продолжает удалять мои ограничения верхнего пробела для кнопки и нижнего пробела, разрывая цепочку между кнопками и разделителями, и заменяя их произвольными ограничениями верхнего пробела в супервизоре, которые бесполезны.
Дести
Тот же самый подход работает для меня в горизонтальном расположении - у меня есть фиксированные представления ширины, разделенные равными видами распорки ширины. Здорово, что мне не нужно смешивать его с любым кодом. Спасибо!
Kamil Nomtek.com
2
Не нужно использовать проставки. Смотрите мой ответ ниже.
smileBot
316

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

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

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

Инструкции:

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

2) Добавьте ограничение по центру от каждой кнопки в суперпредставление:

введите описание изображения здесь

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

введите описание изображения здесь

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

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

введите описание изображения здесь

5) Вот демо-ход!

введите описание изображения здесь

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


Старый ответ


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

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

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

2) Удерживайте нажатой клавишу Shift, так как вы могли бы также добавить другое ограничение, которое мы будем использовать, а именно «руководство по разметке от нижнего пространства к нижнему».

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

Удерживайте клавишу Shift, чтобы добавить эти два ограничения для каждой метки.

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

Вот наши множители.

Label1 = 1/4 = .25,

Label2 = 2/4 = .5,

Label3 = 3/4 = 0,75

(Правка: @Rivera прокомментировал, что вы можете просто использовать отношения прямо в поле множителя, а xCode с помощью math!)

4) Итак, давайте выберем Label1 и выберем нижнее ограничение. Как это: введите описание изображения здесь

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

6) В раскрывающемся списке выберите «Поменять местами первый и второй элемент».

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

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

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

введите описание изображения здесь

10) Повторите этот процесс для каждой метки и вставьте соответствующий множитель: 0,5 для метки 2 и 0,75 для метки 3. Вот конечный продукт во всех направлениях со всеми компактными устройствами! Супер просто. Я смотрел на множество решений, включающих множество кода и разделителей. Это, безусловно, лучшее решение, которое я видел по этому вопросу.

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

введите описание изображения здесь

smileBot
источник
2
Я все еще борюсь с этим. Я думаю, вы должны использовать Xcode 6, потому что в ограничениях Xcode 5 нет опции 'wC hAny'. Когда я следую вашим инструкциям, все подпредставления застряли в верхнем центре.
kraftydevil
4
@kraftydevil AFAIK Нижнее руководство по макету появляется только в раскадровках, а не в XIBS. Используйте «Нижнее пространство для контейнера» в XIBS.
Тимур Кучкаров
6
О - также стоит отметить, что IB в Xcode 6 поддерживает множители коэффициентов, поэтому вы можете добавить такие вещи, как «1: 4», «2: 5», «3: 4» и т. Д.
Benjohn
3
Попытка выяснить, как сделать это по горизонтали - как первый TextView на 1/3 пути слева, а второй TextView 2/3 (остальная часть пути) ... ... (EDIT) получил - сделать первая 1/3 (левая) Trailing-Space to Right Margin, реверс, ноль, присваивать как раньше ...
kfmfe04
3
Для горизонтального интервала просто установите конечные ограничения на суперпредставление для каждого подпредставления, а затем установите множитель отношения, например. 5: 1, 5: 2, 5: 3 и т. Д., Перемещаясь слева направо.
user3344977 30.12.15
98

Очень быстрое решение Interface 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 вида для разметки, это выглядит так:

Иллюстрация о том, как равномерно распределить взгляды в IB

РЕДАКТИРОВАТЬ Примечание: Выбор N:pили p:Nзависит от порядка отношений вашего ограничения выравнивания. Если «Первый элемент» - это Superview.Center, вы можете использовать p:N, а если Superview.Center - «Второй элемент», вы можете использовать N:p. Если вы сомневаетесь, просто попробуйте оба ... :-)

Мета
источник
Это решение не будет отражать дизайн, если вы переключитесь на другой язык
wod
@wod Что ты имеешь в виду? Переключить что на другой язык? А что именно ломается?
Мете
4
Что делать, если элементы не одинакового размера? этот шаблон не будет работать правильно, верно?
ucangetit
2
Я не понимаю, как это решение является правильным? Зазор между краем экрана и внешними элементами отличается от зазора между внешними элементами и центральным элементом?
Майлз
6
Это не для меня "как есть", я должен был поменять множители (например, 3: 1 от первого элемента преобразуется в 1: 3). Просто выбросить это, если это кому-нибудь поможет.
Ces
70

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

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

Глорфиндел
источник
Да, это печально, что у этого нет ретро-совместимости, так что пока ваше приложение не нуждается в поддержке IOS8, ответ @Mete пока лучший.
ZiggyST
51

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

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

Тем не менее, то, что вы пытаетесь сделать, не является простым и будет трудно достичь в конструкторе интерфейсов. Это довольно просто сделать в коде. Этот код 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, но для ясности я проделал длинный путь здесь.

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

jrturton
источник
Вы, сэр, спасатель. Я не понял # 1 до сих пор. Разработчик интерфейса сводит меня с ума, но я думал, что он может делать все то же самое. В любом случае я предпочитаю использовать код, так что это прекрасно, и я получу право на методы категории.
nothappybob
Хотя это определенно довольно близко, это не совсем решает конкретную проблему (сохранение размера представления одинаковым с равномерным интервалом между подпредставлениями). Это решение является уточненным общим ответом.
смайлиборг
21

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

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

[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.m

@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
Бен Долман
источник
1
Он работал путем удаления всех ограничений и сразу после этого, добавляя ограничения Width и Height к этим объектам, а затем вызывая метод constraintsForEvenDistributionOfItems :
lativeToCenterOfItem
@carlos_ms Я хотел бы увидеть ваш код, потому что, когда я пытаюсь использовать четное количество элементов, этот код отлично работает при переключении ориентации устройства. Вы уверены, что у вас случайно нет других ограничений (таких как ограничения авторазмера), которые вызывают конфликт?
Бен Долман
Вот пример создания 4 меток и равномерного распределения их по вертикальной оси: gist.github.com/bdolman/5379465
Бен Долман,
20

Правильный и простой способ - использовать Stack Views.

  1. Добавьте ваши ярлыки / представления в Stack View :

введите описание изображения здесь

  1. Выберите Stack Просмотр и набор Распределение быть равными промежутками :

введите описание изображения здесь

  1. Добавьте Spacing к ограничениям ближайшего соседа в Stack View и обновите фреймы:

введите описание изображения здесь

  1. Добавьте ограничения высоты ко всем меткам (необязательно). Требуется только для представлений, которые не имеют внутреннего размера). Например, для меток здесь не нужны ограничения по высоте, а нужно только установить numberOfLines = 3 или 0, например.

введите описание изображения здесь

  1. Предварительный просмотр:

введите описание изображения здесь

Олег Кудинов
источник
2
Это> = только iOS 9.0, поэтому проверьте цель развертывания, прежде чем приступить к реализации :) Приятно видеть, что это было добавлено!
DivideByZer0
1
2018 сейчас, используйте представление стека
Jingshao Чен
17

Проверьте библиотеку с открытым исходным кодом 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 для ограничений.)

smileyborg
источник
Хорошая работа! Хотя вам, вероятно, следует переименовать репо, чтобы сделать его доступным через cocoapods.
Джртуртон
9

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

Посмотреть интервал иллюстрации

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

Тем не менее, я еще не нашел хорошего общего решения. В настоящее время моя лучшая идея состоит в том, чтобы вставить «промежуточные виды» между подпредставлениями и установить равные высоты пространственных видов.

Florian
источник
1
Я использовал решение скрытых видов распорок, которое хорошо работает.
paulmelnikow
8

Мне удалось решить это полностью в IB:

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

Поэтому, если у вас есть три метки, установите множители для каждого из этих ограничений на 0,1666667, 0,5, 0,833333.

hayesk
источник
1
Я фактически должен был сделать 1 / n 3 / n 5 / n 7 / n upto (2n-1) / n
Хамза Малик
5

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

Начальный XIB

Растянутый XIB

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

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

Оуэн Годфри
источник
4

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

+(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;
}
Рэй W
источник
3

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

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

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

Johannes
источник
3

версия 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)
Сарат Равендран
источник
1
Разработка того, как этот код отвечает на вопросы, поможет будущим посетителям.
JAL
2

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

Я установил свои взгляды на 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 Format, чтобы установить ограничения:

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



}

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

Я надеюсь, что это помогает.

Marcal
источник
2

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

В этом примере 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;
    }
}
Кевин Коннер
источник
Это лучшее решение при использовании разных размеров видов, которые должны быть отцентрированы.
noobsmcgoobs
2

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

То, что я закончил, было:

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

Шаг 1: расположение кнопок

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

Шаг 2: добавить ведущие ограничения пространства

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

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

Шаг 3 и 4: измените множитель на xPos / screenWidth и установите второй элемент на .Trailing

ucangetit
источник
2

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

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]];
zgjie
источник
2

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

  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 { $0.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 { $0.active = true }

        return view
    }
}

введите описание изображения здесь

И снова, это может быть сделано довольно легко с 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 { $0.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 уменьшает объем кода и выглядит красиво.

Sandeep
источник
1

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

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

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

Colin
источник
1

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

Установите метку по центру (label2) на «Горизонтальный центр в контейнере» и «Вертикальный центр в контейнере»

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

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

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

В результате все 4 расстояния изменят свои размеры в равной степени.

sust86
источник
2
Я обнаружил, что это работает только тогда, когда вы изменяете размер суперпредставления так, чтобы интервал принимал ровно минимальное или максимальное значения. Когда вы изменяете размер до значения где-то между, подпредставления распределяются неравномерно.
Дориан Рой
1

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

 [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.m

#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
полутень
источник
1

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

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

А вот портретная версия:введите описание изображения здесь

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

Джеймс
источник
1

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

введите описание изображения здесь

Владимир Славик
источник
1

Поздно до вечеринки, но у меня есть рабочее решение для создания меню горизонтально с интервалом. Это можно легко сделать используя ==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;
}
tsuz
источник
0

В Android есть метод объединения видов в систему на основе ограничений, которую я хотел имитировать. Поиски привели меня сюда, но ни один из ответов не сработал. Я не хотел использовать StackViews, потому что они имеют тенденцию вызывать у меня больше горя в будущем, чем экономить сразу. Я закончил тем, что создал решение, которое использовало UILayoutGuides, помещенное между представлениями. Управление их шириной позволяет использовать различные типы дистрибутивов, стили цепочек на языке Android. Функция принимает начальный и конечный якорь вместо родительского представления. Это позволяет размещать цепочку между двумя произвольными представлениями, а не распределять внутри родительского представления. Он использует, UILayoutGuideкоторый доступен только в iOS 9+, но это больше не должно быть проблемой.

public enum LayoutConstraintChainStyle {
    case spread //Evenly distribute between the anchors
    case spreadInside //Pin the first & last views to the sides and then evenly distribute
    case packed //The views have a set space but are centered between the anchors.
}

public extension NSLayoutConstraint {

    static func chainHorizontally(views: [UIView],
                                  leadingAnchor: NSLayoutXAxisAnchor,
                                  trailingAnchor: NSLayoutXAxisAnchor,
                                  spacing: CGFloat = 0.0,
                                  style: LayoutConstraintChainStyle = .spread) -> [NSLayoutConstraint] {
    var constraints = [NSLayoutConstraint]()
    guard views.count > 1 else { return constraints }
    guard let first = views.first, let last = views.last, let superview = first.superview else { return constraints }

    //Setup the chain of views
    var distributionGuides = [UILayoutGuide]()
    var previous = first
    let firstGuide = UILayoutGuide()
    superview.addLayoutGuide(firstGuide)
    distributionGuides.append(firstGuide)
    firstGuide.identifier = "ChainDistribution\(distributionGuides.count)"
    constraints.append(firstGuide.leadingAnchor.constraint(equalTo: leadingAnchor))
    constraints.append(first.leadingAnchor.constraint(equalTo: firstGuide.trailingAnchor, constant: spacing))
    views.dropFirst().forEach { view in
        let g = UILayoutGuide()
        superview.addLayoutGuide(g)
        distributionGuides.append(g)
        g.identifier = "ChainDistribution\(distributionGuides.count)"
        constraints.append(contentsOf: [
            g.leadingAnchor.constraint(equalTo: previous.trailingAnchor),
            view.leadingAnchor.constraint(equalTo: g.trailingAnchor)
        ])
        previous = view
    }
    let lastGuide = UILayoutGuide()
    superview.addLayoutGuide(lastGuide)
    constraints.append(contentsOf: [lastGuide.leadingAnchor.constraint(equalTo: last.trailingAnchor),
                                    lastGuide.trailingAnchor.constraint(equalTo: trailingAnchor)])
    distributionGuides.append(lastGuide)

    //Space the according to the style.
    switch style {
    case .packed:
        if let first = distributionGuides.first, let last = distributionGuides.last {
            constraints.append(first.widthAnchor.constraint(greaterThanOrEqualToConstant: spacing))
            constraints.append(last.widthAnchor.constraint(greaterThanOrEqualToConstant: spacing))
            constraints.append(last.widthAnchor.constraint(equalTo: first.widthAnchor))
            constraints.append(contentsOf:
                distributionGuides.dropFirst().dropLast()
                    .map { $0.widthAnchor.constraint(equalToConstant: spacing) }
                )
        }
    case .spread:
        if let first = distributionGuides.first {
            constraints.append(contentsOf:
                distributionGuides.dropFirst().map { $0.widthAnchor.constraint(equalTo: first.widthAnchor) })
        }
    case .spreadInside:
        if let first = distributionGuides.first, let last = distributionGuides.last {
            constraints.append(first.widthAnchor.constraint(equalToConstant: spacing))
            constraints.append(last.widthAnchor.constraint(equalToConstant: spacing))
            let innerGuides = distributionGuides.dropFirst().dropLast()
            if let key = innerGuides.first {
                constraints.append(contentsOf:
                    innerGuides.dropFirst().map { $0.widthAnchor.constraint(equalTo: key.widthAnchor) }
                )
            }
        }
    }

    return constraints
}
Сэм Кордер
источник
0

Я хотел выровнять 5 изображений по горизонтали, поэтому в итоге последовал ответ Мете с небольшой разницей.

Первое изображение будет центрировано по горизонтали в контейнере, равном 0 и множителю 1: 5:

первое изображение

Второе изображение будет центрировано горизонтально в контейнере, равном 0, и множителе 3: 5: второе изображение

И так для остальных изображений. Например, пятое (и последнее) изображение будет горизонтально отцентрировано в контейнере, равном 0, и множителе 9: 5: последнее изображение

Как объяснил Мете, порядок идет 1, 3, 5, 7, 9 и т. Д. Позиции следуют той же логике: первая позиция - 1, затем пробел, затем следующая позиция 3 и т. Д.

Адриана Пинеда
источник
-1

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

Джош О'Коннор
источник
Не понимаю отрицательный голос, мое решение нестандартно. Зачем испытывать головную боль при создании списка с автопоставкой, когда какао дает вам список в UITableView?
Джош О'Коннор