Рівномірно розміщуйте кілька переглядів у вікні контейнера


279

Автоматичний макет ускладнює моє життя. Теоретично, коли я переключався, це було б дуже корисно, але я, здається, постійно борюся з цим.

Я зробив демонстраційний проект, щоб спробувати знайти допомогу. Хтось знає, як зробити так, щоб пробіли між видами збільшувались або зменшувалися рівномірно, коли розмір перегляду змінювався?

Ось три мітки (розміщені вручну вертикально рівними):

зображення1

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

зображення2


можливо встановити обмеження від однієї мітки до іншої, і встановити їх співвідношення "більше, ніж дорівнює", буде корисно
BergP

програмно, наприклад, використовуючи цей метод NSLayoutConstraint: + (id) constraintWithItem: (id) атрибут view1: (NSLayoutAttribute) attr1, пов'язанийBy: (NSLayoutRelation) відношення доItem: (id) view2 атрибут: (NSLayoutAttribute) attr2 множник: (CGF множник) (CGF постійна: (CGFloat) c
BergP

Я відкриваю загальну заміну UITabBar, яка узагальнює цей підхід за допомогою автоматичного макета. Ви можете перевірити тести, щоб побачити, як я створюю свої обмеження щодо автоматичного макета. GGTabBar
Goles

Відповіді:


211

Тож мій підхід дозволяє зробити це в конструкторі інтерфейсів. Що ви робите, це створити "прокладки", які ви встановили так, щоб вони однаково відповідали висоті. Потім додайте верхній і нижній обмеження до міток (див. Скріншот).

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

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

Примітка. Будьте впевнені, що у вас немає додаткових обмежень на верхній / нижній простір ваших міток для перегляду; лише ті, що стосуються "космічних поглядів". Це буде задовільним, оскільки верхнє та нижнє обмеження знаходяться на "Space View 1" та "Spacer View 4" відповідно.

Дух 1: Я продублював погляд і просто перевів його в ландшафтний режим, щоб ви могли бачити, що він працює.

Дух 2: "Погляди прокладки" могли бути прозорими.

Дух 3: Цей підхід можна застосувати горизонтально.


10
Це працює. Насправді вигляд спейсерів можна приховати, і вони все одно створюватимуть правильний макет.
paulmelnikow

Це було дуже корисно. Як і коментар про те, щоб просто позначити види проставки як приховані. Тоді ви все ще можете їх бачити у своїй раскадровці / xib, але вони не відображаються у вашому додатку. Дуже хороша.
shulmey

Мені це вдалося майже попрацювати після кількох годин неодноразових спроб. На жаль, Xcode продовжує видаляти мої обмеження "від простору до кнопки" та "нижнього простору", розриваючи ланцюжок між кнопками та прокладками та замінюючи їх довільними обмеженнями "від простору до перегляду", які марні.
Desty

Такий самий підхід працює для мене в горизонтальному розташуванні - у мене є види фіксованої ширини, розділені рівними розглядами ширини. Чудово, що мені не потрібно змішувати його з будь-яким кодом. Дякую!
Kamil Nomtek.com

2
Не потрібно використовувати розпірки. Дивіться мою відповідь нижче.
усмішкаБот

316

ШУКАЙТЕ, НЕ СПАСЕРІВ!

На основі пропозицій у розділі коментарів моєї оригінальної відповіді, особливо корисних пропозицій @ Rivera, я спростив свою оригінальну відповідь.

Я використовую gifs, щоб проілюструвати, наскільки це просто. Сподіваюсь, ви знайдете корисні графічні файли. На всякий випадок, якщо у вас є проблеми з gifs, я включив стару відповідь нижче звичайними знімками екрана.

Інструкції:

1) Додайте свої кнопки або мітки. Я використовую 3 кнопки.

2) Додайте до контрольного огляду центральне обмеження x від кожної кнопки:

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

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

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

4) Відрегулюйте обмеження, додане в №3 вище, наступним чином:

а) виберіть обмеження; б) вилучіть константу (встановлено 0), в) змініть множник так: візьміть кількість кнопок + 1 і, починаючи вгорі, встановіть множник як кнопкуCountPlus1: 1 , а потім кнопкуCountPlus1 : 2 , і нарешті кнопкаCountPlus1: 3 . (Я пояснюю, звідки я взяв цю формулу в старій відповіді нижче, якщо вас цікавить).

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

5) Ось демонстрація працює!

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

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


Стара відповідь


Незважаючи на те, що кажуть документи Apple і чудова книга Еріки Садун ( Auto Layout Demystified ), можна рівномірно проглядати місця без пробілів . Це дуже просто зробити в IB та в коді для будь-якої кількості елементів, які ви хочете розподілити рівномірно. Все, що вам потрібно, - це математична формула, яка називається "формула розділу". Це зробити простіше, ніж пояснити. Я зроблю все можливе, демонструючи це в IB, але це так само просто зробити в коді.

У відповідному прикладі ви б

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

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

3) Виберіть "посібник з макету від нижнього місця до низу" та "горизонтальний центр по контейнеру". Зробіть це для всіх 3-х міток.

Утримуйте зрушення, щоб додати ці два обмеження для кожної мітки

В основному, якщо ми беремо мітку, координату якої ми хочемо визначити, і розділимо її на загальну кількість міток плюс 1, то у нас є число, яке ми можемо додати до IB, щоб отримати динамічне розташування. Я спрощую формулу, але ви можете використовувати її для встановлення горизонтального проміжку або одночасно і вертикального, і горизонтального. Це супер потужно!

Ось наші множники.

Мітка1 = 1/4 = .25,

Мітка2 = 2/4 = .5,

Мітка3 = 3/4 = .75

(Редагувати: @Rivera прокоментував, що ви можете просто використовувати коефіцієнти безпосередньо в полі множника, а xCode з робити математику!)

4) Отже, виберемо Label1 і виберемо нижнє обмеження. Подобається це: введіть тут опис зображення

5) Виберіть "Другий елемент" у Ревізорі атрибутів.

6) З випадаючого списку виберіть "Зворотний перший і другий елемент".

7) Нульова константа і значення wC hAny. (Ви можете додати сюди зміщення, якщо воно знадобиться).

8) Це найважливіша частина. У поле множника додаємо наш перший множник 0,25.

9) Поки ви знаходитесь на ньому, встановіть верхній "Перший елемент" на "ЦентрY", оскільки ми хочемо відцентрувати його до центру y мітки. Ось як все це має виглядати.

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

10) Повторіть цей процес для кожної мітки та підключіть відповідний множник: 0,5 для Label2 та 0,75 для Label3. Ось кінцевий продукт у всіх напрямках із усіма компактними пристроями! Супер просто. Я розглядав безліч рішень, що стосуються коду і пробілів коду. Це далеко не найкраще рішення, яке я бачив у цьому питанні.

Оновлення: @kraftydevil додає, що керівництво по макеті знизу відображається лише на сценаріях, а не в xibs. Використовуйте "Простір у нижній частині контейнера" ​​у xibs. Гарний улов!

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


2
Я все ще борюся з цим. Я думаю, що ви повинні використовувати Xcode 6, оскільки в обмеженнях Xcode 5 немає "wC hAny". Коли я виконую ваші вказівки, усі підзагляди застрягають у верхньому центрі.
kraftydevil

4
@kraftydevil AFAIK Нижній посібник із розмітки відображається лише на дошках, а не в xibs. Використовуйте "Простір у нижній частині контейнера" ​​у xibs.
Тимур Кучкаров

6
О - також варто зазначити, що IB у Xcode 6 підтримує множники співвідношення, тому ви можете вводити такі речі, як "1: 4", "2: 5", "3: 4" тощо.
Бенджон

3
Намагаючись зрозуміти, як це зробити горизонтально - як перший TextView 1/3 шляху зліва, а другий TextView 2/3 (решта шляху) ... ... (EDIT) отримав це - зробіть перша 1/3 (зліва) Космічний пробіл до правої межі, реверс, нуль, призначення, як раніше ...
kfmfe04

3
Для горизонтального інтервалу просто встановіть обмеження з обмеженням на огляд для кожного підперегляду, а потім встановіть множник відношення, наприклад. 5: 1, 5: 2, 5: 3 тощо, рухаючись зліва направо.
user3344977

98

Дуже швидке рішення для створення інтерфейсів:

Щоб будь-яка кількість переглядів була рівномірно розподілена в межах огляду, просто дайте кожному обмеження "Вирівнювання центру 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, ...]. Працює для будь-якої кількості переглядів.

Отже, якщо у вас є три види на простір, це виглядає приблизно так:

Ілюстрація того, як рівномірно розподілити погляди на ІБ

EDIT Примітка. Вибір N:pабо p:Nзалежить від порядку відношення обмеження для вирівнювання. Якщо "Перший елемент" - це Superview.Center, ви можете використовувати p:N, тоді як якщо Superview.Center - "Другий елемент", ви можете використовувати його N:p. Якщо ви сумніваєтесь, просто спробуйте обидва ... :-)


Це рішення не буде відображати дизайн, якщо ви перейдете на іншу мову
wod

@wod Що ти маєш на увазі? Переключити що на іншу мову? А що саме ламається?
Мете

4
що робити, якщо елементи не однакового розміру? ця схема не працюватиме правильно?
ucangetit

2
Я не розумію, наскільки це рішення правильне? Зазор між краєм екрана та зовнішніми елементами відрізняється від зазору між зовнішніми елементами та центральним елементом?
myles

6
Це не було для мене "як є", мені довелося перевернути множники (наприклад, 3: 1 з першого пункту перетворюється на 1: 3). Просто викинувши це, якщо це комусь допоможе.
Чес

70

Що стосується iOS 9, Apple зробила це дуже легко з (довгоочікуваними) UIStackView. Просто виберіть представлення даних, які ви хочете містити в Інтерфейсі та виберіть Редактор -> Вставити -> Перегляд стека. Встановіть відповідні обмеження ширини / висоти / поля для подання стека та переконайтеся, що для властивості розподілу встановлено значення "Рівний пробіл":

Звичайно, якщо вам потрібно підтримувати 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 , але для наочності я це зробив довгим шляхом.

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


Ви, пане, рятівник. Я досі не зрозумів №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:
RelaToCenterOfItem

@carlos_ms Я хотів би побачити ваш код, тому що коли я намагаюся з парною кількістю предметів, цей код чудово працює при переключенні орієнтацій пристроїв. Ви впевнені, що у вас ненавмисно відсутні деякі інші обмеження (наприклад, автоматичне обмеження обмежень), які викликають конфлікт?
Бен Долман

Ось приклад створення 4 міток і рівномірного розташування їх по вертикальній осі: gist.github.com/bdolman/5379465
Ben

20

Правильний і найпростіший спосіб - це використання перегляду стека.

  1. Додайте свої мітки / представлення до перегляду стека :

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

  1. Виберіть «Перегляд стека» та встановіть « Розподіл» на рівні міжряддя :

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

  1. Додайте пробіли до найближчих сусідських обмежень до перегляду стека та оновіть кадри:

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

  1. Додати обмеження висоти до всіх міток (необов’язково). Потрібно лише для представлень, що не мають внутрішнього розміру). Наприклад, мітки не потребують обмежень по висоті, а потрібно встановити лише числоOfLines = 3 або 0.

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

  1. Насолоджуйтесь попереднім переглядом:

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


2
Це лише> = iOS 9.0, тому перевірте свою ціль розгортання, перш ніж почати впроваджувати :) Приємно бачити, що це додано, хоча!
DivideByZer0

1
2018 рік, використовуйте перегляд стека
Jingshao Chen,

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 обмежень.)


Хороша робота! Хоча вам, мабуть, слід перейменувати репо, щоб ви могли зробити його доступним через какаоподи.
jrturton

9

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

Перегляд інтервалу ілюстрації

Є 3 види, всі 20pt заввишки. Використовуючи будь-який із запропонованих методів, однаково розподіляє центри поглядів і надає вам ілюстроване компонування. Зауважте, що y-центр поглядів розташований однаково. Однак проміжок між переглядом та видом зверху становить 15pt, тоді як інтервал між підглядами - лише 5pt. Щоб види були однаково розташовані, вони повинні бути 10pt, тобто всі сині стрілки повинні бути 10pt.

Тим не менш, я ще не придумав хорошого загального рішення. В даний час моя найкраща ідея - вставити "інтервали між видами" між підглядами та встановити висоту інтервалів подання рівними.


1
Я використав рішення прихованих пробілів, що добре працює.
paulmelnikow

8

Мені вдалося вирішити це повністю в IB:

  1. Зробіть обмеження, щоб вирівняти центр Y кожного з ваших підглядів до нижнього краю перегляду.
  2. Встановіть множник кожного з цих обмежень на 1 / 2n, 3 / 2n, 5 / 2n, ..., n-1 / 2n, де n - кількість підпереглядів, які ви поширюєте.

Отже, якщо у вас є три мітки, встановіть множники для кожного з цих обмежень на 0,1666667, 0,5, 0,833333.


1
Я фактично повинен був зробити 1 / n 3 / n 5 / n 7 / n upto (2n-1) / n
Hamzah Malik

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

3

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


1
Після випуску iOS 9, що UIStackView найкращим варіантом для виконання цього завдання, Apple видалила цю сторінку
Мехул Тхакар

3

З етикетками це працює чудово як мінімум:

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

Якщо перший має таку ж ширину, як другий, а другий третій, а третій перший, то всі вони отримають однакову ширину ... Ви можете це робити як по горизонталі (Н), так і по вертикалі (V).


3

швидка версія 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 для встановлення обмежень:

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



}

Хороша річ у цьому методі полягає в тому, що ви повинні робити дуже мало математики. Я не кажу, що це ідеальне рішення, але я працюю над компонуванням, якого я намагався досягти.

Я сподіваюся, що це допомагає.


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) Спочатку розміщення моїх кнопок на екрані шириною 320 пікселів розподіляло так, як я хотіла, щоб це виглядало на пристрої 320 пікселів.

крок 1: розміщення кнопок

2) Тоді я додав провідне обмеження на простір для нагляду за всіма своїми кнопками.

крок 2: додайте провідні обмеження в просторі

3) Потім я змінив властивості провідного простору так, щоб константа була 0, а множник - це зміщення x, поділене на ширину екрана (наприклад, моя перша кнопка була 8 пікселів від лівого краю, тому я встановила свій множник на 8/320)

4) Тоді важливим кроком тут є зміна другого пункту у обмежувальному відношенні на нагляд superview.Trailing замість superview.leading. Це важливо, тому що superview.Leading дорівнює 0, а в моєму випадку дорівнює 320, тож 8/320 - це 8 px на пристрої 320px, тоді, коли ширина нагляду змінюється на 640 або будь-що інше, усі перегляди рухаються у співвідношенні відносно ширини розміром екрана 320 пікселів. Математику тут зрозуміти набагато простіше.

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


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

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 зменшує деякий код і виглядає приємно.


1

Іншим підходом може бути те, що верхня і нижня мітки мають обмеження відносно верхнього та нижнього виду відповідно, а середній вигляд має обмеження верхнього та нижнього відносно першого та третього виду відповідно.

Зауважте, що ви маєте більше контролю над обмеженнями, ніж це може здатися, перетягуючи погляди близько один до одного, поки не з’являться напрямні штрихові лінії - вони вказують на обмеження між двома об'єктами, які будуть утворюватися замість між об'єктом та суперпроглядом.

У цьому випадку ви хочете змінити обмеження на "Більше або рівне" бажаного значення, а не "рівне", щоб дозволити їм змінювати розмір. Не впевнений, чи це зробить саме те, що ви хочете.


1

Дуже простий спосіб вирішити це в InterfaceBuilder:

Встановіть по центру мітку (label2) на "Горизонтальний центр у контейнері" та "Вертикальний центр у контейнері"

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

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

Крім того, ви можете також додати два обмеження для label1 - Top Space To SuperView і два обмеження для label2 - Bottom Space To SuperView.

Результатом буде те, що всі 4 інтервали будуть однаково змінювати свої розміри.


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, але voilà!

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

Навіть розподіл

#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 мітки 2 до нагляду, щоб воно було закріплено по центру. Потім встановіть вертикальний простір мітки 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;
}

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

Чому ви просто не створюєте tableView і робите isScrollEnabled = false


Не розумію суті, моє рішення - це думка поза межами. Навіщо переживати головний біль створення списку з автоматичним розкладом, коли какао надає вам список у UITableView?
Джош О'Коннор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.