Равномерно распределить несколько видов в контейнере
Auto Layout делает мою жизнь сложной. Теоретически, это было бы очень полезно, когда я переключусь, но я, кажется, борюсь с этим все время.
Я создал демо-проект, чтобы попытаться найти помощь. Кто-нибудь знает, как сделать промежутки между представлениями увеличиваться или уменьшаться равномерно при изменении размера представления?
вот три метки (вручную разнесенные по вертикали):
Я хочу, чтобы они изменили размер своего интервала (не размер вида) равномерно при вращении. По умолчанию верхний и Нижний виды хлюпают по направлению к центру:
27 ответов
поэтому мой подход позволяет вам сделать это в interface builder. Что вы делаете, так это создаете "распорные виды", которые вы установили для соответствия высотам одинаково. Затем добавьте верхние и нижние ограничения к меткам (см. скриншот).
более конкретно, у меня есть верхнее ограничение на "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:
3) добавьте ограничение от каждой кнопки к нижнему ограничению макета:
4) отрегулируйте ограничение, добавленное в #3 выше следующим образом:
a) выберите ограничение, b) удалить константу (значение 0), c) измените множитель следующим образом: возьмите количество кнопок + 1 и, начиная сверху, установите множитель как buttonCountPlus1: 1, а потом buttonCountPlus1: 2 и наконец buttonCountPlus1: 3. (Я объясняю, откуда я взял эту формулу в старом ответе ниже, если вы заинтересованный.)
5) вот демо работает!
Примечание: Если ваши кнопки имеют большую высоту, вам нужно будет компенсировать это в постоянном значении, так как ограничение находится внизу кнопки.
Ответ
несмотря на то, что документы Apple и отличная книга Эрики Садун (Автоматическая Разметка Демистифицирована) говорят, это возможно равномерное пространство просмотров без проставки. Это очень просто сделать в IB и в код для любого количества элементов, которые нужно равномерно. Все, что вам нужно, это математическая формула, называемая "формула раздела". Это проще сделать, чем объяснить. Я постараюсь продемонстрировать это в IB, но это так же легко сделать в коде.
в рассматриваемом примере вы бы
1) Начните с установки каждой метки для ограничения центра. Это очень просто делать. Просто управляйте перетаскиванием от каждой метки вниз.
2) удерживайте shift, так как вы также можете добавить другое ограничение, которое мы будем использовать, а именно "нижнее пространство для нижнего руководства по компоновке".
3) Выберите "нижнее пространство для нижней направляющей компоновки "и"центр по горизонтали в контейнере". Сделайте это для всех 3 меток.
в принципе, если мы возьмем метку, координату которой мы хотим определить, и разделим ее на общее число меток плюс 1, тогда у нас есть число, которое мы можем добавить в IB, чтобы получить динамическое местоположение. Я упрощаю формулу, но вы можете использовать ее для установки горизонтального расстояния или вертикального и горизонтального одновременно. Это супер мощный!
вот наши мультипликаторы.
Label1 = 1/4 = .25,
Label2 = 2/4 = .5,
Label3 = 3/4 = .75
(Edit: @Rivera прокомментировал, что вы можете просто использовать коэффициенты непосредственно в множителе поле и xCode с математикой!)
4) Итак, давайте выберем Label1 и выберем нижнее ограничение. Вроде этого:
5) выберите" второй элемент " в Инспекторе атрибутов.
6) в раскрывающемся списке выберите "обратный первый и второй элемент".
7) обнулить константу и значение wC hAny. (Вы можете добавить смещение здесь, Если вам это нужно).
8) это критическая часть: в поле множителя добавьте наш первый множитель 0.25.
9) в то время как вы на нем установите верхний "первый элемент" в "CenterY", так как мы хотим центрировать его в y-центр метки. Вот как все это должно выглядеть.
10) повторите этот процесс для каждой этикетки и подключите соответствующий множитель: 0.5 для Label2 и 0.75 для Label3. Вот конечный продукт во всех ориентациях со всеми компактными устройствами! Очень просто. Я рассматривал много решений, связанных с пачками кода, и распорки. Это далеко не лучшее решение, которое я видел по этому вопросу.
Update: @kraftydevil добавляет, что нижнее руководство по компоновке отображается только в раскадровках, а не в xibs. Использовать нижнее пространство для контейнера в xibs. Хороший улов!
очень быстрый интерфейс 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 видом в космос, это выглядит так:
редактировать Примечание: выбор N:p
или p:N
зависит от порядка отношений вашего ограничения выравнивания. Если" первый элемент " - Superview.Центр, вы можете использовать p:N
, в то время как если Superview.Центр - " второй Пункт", вы можете использовать N:p
. Если сомневаетесь, попробуйте оба варианта... :-)
начиная с iOS 9, Apple сделала это очень легко с (долгожданным) UIStackView
. Просто выберите представления, которые вы хотите содержать в Построителе интерфейса, и выберите редактор - > встроить в - > вид стека. Установите соответствующие ограничения ширины / высоты / поля для представления стека и убедитесь, что свойство Distribution имеет значение "равный интервал":
конечно, если вам нужна поддержка iOS 8 или ниже, вам придется выбрать один из других опции.
Я был на американских горках любви autolayout и ненавижу его. Ключом к любви, по-видимому, является принятие следующего:
- редактирование и "полезное" автоматическое создание ограничений Interface builder практически бесполезно для всех, кроме самого тривиального случая
- создание категорий для упрощения общих операций является спасением жизни, поскольку код настолько повторяющийся и подробный.
тем не менее, то, что вы пытаетесь не просто и было бы трудно достичь в 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
// ...
/** 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
для ограничений.)
правильный и простой способ-использовать представления стека.
- добавить метки/вид Посмотреть Стек:
- выберите Вид стека и установите распределение на Равный Интервал:
- добавить расстояние до ближайшего соседа ограничения на представление стека и обновление кадров:
- добавить Высота ограничения для всех меток:
- Наслаждайтесь Просмотром:
У меня аналогичная проблема и обнаружил этот пост. Однако ни один из представленных в настоящее время ответов не решает проблему так, как вы хотите. Они не делают интервал равным, а скорее распределяют центр меток поровну. Важно понимать, что это не одно и то же. Я построил небольшую диаграмму, чтобы проиллюстрировать это.
есть 3 вида, все 20pt высотой. Использование любого из предложенных методов равномерно распределяет центры видов и дают вам иллюстрированный макет. Обратите внимание, что y-центр представлений разнесен поровну. Однако расстояние между супервизором и видом сверху составляет 15pt, а расстояние между подвидами-всего 5pt. Чтобы виды были разнесены одинаково, они должны быть 10pt, т. е. все синие стрелки должны быть 10pt.
тем не менее, я еще не придумал хорошее общее решение. В настоящее время моя лучшая идея-вставить "дистанционирование" между подвидами и установить высоты представлений интервалов должны быть равными.
Я смог решить это полностью В IB:
- сделайте ограничения для выравнивания центра Y каждого из ваших подвидов к нижнему краю супервизора.
- установите множитель каждого из этих ограничений в 1 / 2n, 3/2n, 5/2n,..., n-1/2n, где n-количество подвидов, которые вы распространяете.
поэтому, если у вас есть три метки, установите множители для каждого из этих ограничений в 0.1666667, 0.5, 0.833333.
Я нашел идеальный и простой метод. Автоматическая компоновка не позволяет изменять размер пространств поровну, но позволяет изменять размер представлений поровну. Просто поместите некоторые невидимые виды между вашими полями и скажите auto layout, чтобы сохранить их одинаковый размер. Он работает отлично!
одно замечание; когда я уменьшил размер в дизайнере интерфейса, иногда он запутался и оставил метку там, где она была, и у нее был конфликт, если размер был изменен на нечетную величину. В остальном он работал отлично.
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.
2) Затем я добавил ведущее ограничение пространства для просмотра на всех моих кнопки.
3) Затем я изменил свойства ведущего пространства так, чтобы константа была 0, а множитель-смещение x, деленное на ширину экрана (например, моя первая кнопка была 8px от левого края, поэтому я установил множитель на 8/320)
4) тогда важным шагом здесь является изменение второго элемента в отношении ограничения, чтобы быть супервизором.Трейлинг вместо superview.ведущий. Это ключ, потому что superview.Ведущий-0 и трейлинг в моем случае 320, поэтому 8/320-8 px на устройстве 320px, а затем, когда ширина супервизора изменяется на 640 или что-то еще, все виды перемещаются в соотношении к ширине размера экрана 320px. Математика здесь гораздо проще для понимания.
многие ответы не являются правильными, но получить много подсчетов. Здесь я просто пишу решение программно, три вида горизонтально выравниваются,без использования 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
}
}
и опять же, это можно сделать довольно легко с помощью 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 установлено в стандарт:
и вот портретная версия:
Я понимаю, что они не совсем 100% одинаково расположены между базовыми линиями из-за разницы между стандартным пространством между метками и стандартным пространством для супервизора. Если это беспокоит вас, установите размер в 0 вместо стандартного
Я устанавливаю значение ширины только для первого элемента (>=ширина) и минимальное расстояние между каждым элементом (>=расстояние). Затем я использую Ctrl для перетаскивания второго, третьего... item on the first для цепочки зависимостей между элементами.
опоздал на вечеринку, но у меня есть рабочее решение для создания меню по горизонтали с шагом. Это можно легко сделать с помощью ==
в 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;
}