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

Пружины в автоматическом макете: равномерно распределять представления с ограничениями в Xcode 5

Я понимаю старый метод Struts and Springs для выравнивания, калибровки и распределения представлений в Interface Builder. Тем не менее, я не могу понять, как равномерно распределять представления с помощью автоматической компоновки с Xcode 5. Был способ сделать это с помощью Xcode 4, но этот вариант не был.

У меня есть 7 кнопок, расположенных в вертикальном стеке. На 3,5-дюймовом макете он выглядит великолепно. Когда я просматриваю экран в 4-дюймовом макете, все кнопки остаются плотно упакованными, а под последней кнопкой располагается большое пространство.

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

enter image description here

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

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

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

4b9b3361

Ответ 1

EDIT Обратите внимание, что в iOS 9 эта техника станет ненужной, потому что UIStackView автоматически выполнит распределение. Я добавлю еще один ответ, объясняющий, как это работает.

Как выполнить равномерное распределение с использованием автоопределения

Самый простой способ сделать это только в Interface Builder (а не в создании ограничений в коде) - использовать "spacer":

  • Поместите верхнюю и нижнюю кнопки абсолютно.

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

  • Сделайте ограничения между каждой кнопкой и надписью spacer выше и ниже нее с константой 0.

  • Теперь выберите все проставки и установите их высоты равными.

Первый снимок экрана показывает мне установку этого в IB:

enter image description here

Я намеренно не исправил "неулокальные взгляды", потому что хочу, чтобы вы увидели, как это выглядит, когда я разрабатываю ограничения. Здесь результат как на 4-дюймовом, так и на 3,5-дюймовом экране:

enter image description here

Я оставил простыни просмотров черный, просто чтобы показать вам, как работает эта техника, но, конечно, в реальной жизни вы сделали бы их прозрачными и, следовательно, невидимыми! Таким образом, пользователь видит только ваши кнопки, равномерно распределенные на любой высоте экрана.

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

Другие подходы

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

Например, если у нас есть (возможно, невидимый) супервизор, высота которого выступает в качестве границы, чтобы диктовать максимальное вертикальное распределение наших четырех кнопок, мы можем привязать их вершины к вертикальному центру этого супервизора с помощью constant of 0, но a multiplier из 0.000001, 0.666667, 1.33333 и 2.0 соответственно (если у нас есть четыре кнопки); теперь кнопки будут оставаться вертикально распределенными, даже если супервизор изменяет размер в ответ на высоту экрана или что-то еще. [В Xcode 5.1 это можно будет установить в Interface Builder, но в более ранних версиях Xcode это невозможно.]

Ответ 2

В iOS 9/Xcode 7 эта проблема будет тривиально решена в IB. Просто выберите кнопки (или все, что вы хотите распределить по вертикали) и выберите "Редактор" > "Вставить" > "Просмотр стека". Затем вы просто настраиваете представление стека:

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

  • Установите атрибуты представления стека. В этом случае мы хотим, чтобы вертикальная ось, выравнивание заливки, распределение с равным интервалом.

Это все! Тем не менее, вам может быть интересно, как это работает, потому что по-прежнему можно сделать то же самое вручную в коде. Просмотр стека выполняет распределение, а не путем вставки прокладок, но путем вставки распорных направляющих. Руководство (UILayoutGuide) - это легкий объект, который ведет себя как представление для целей ограничений компоновки, но не является представлением и поэтому не должен быть невидимым и не несет никаких накладных расходов на представление.

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

  • Все они имеют абсолютные ограничения высоты

  • Их левая сторона прикреплена к супервину слева, а их право закреплено над правом надписи

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

Теперь предположим, что у нас есть ссылки на четыре представления как views, массив. Тогда:

let guides = [UILayoutGuide(), UILayoutGuide(), UILayoutGuide()]
for guide in guides {
    self.view.addLayoutGuide(guide)
}
NSLayoutConstraint.activateConstraints([
    // guide heights are equal
    guides[1].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
    guides[2].heightAnchor.constraintEqualToAnchor(guides[0].heightAnchor),
    // guide widths are arbitrary, let say 10
    guides[0].widthAnchor.constraintEqualToConstant(10),
    guides[1].widthAnchor.constraintEqualToConstant(10),
    guides[2].widthAnchor.constraintEqualToConstant(10),
    // guide left is arbitrary, let say superview margin
    guides[0].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    guides[1].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    guides[2].leftAnchor.constraintEqualToAnchor(self.view.leftAnchor),
    // bottom of each view is top of following guide
    views[0].bottomAnchor.constraintEqualToAnchor(guides[0].topAnchor),
    views[1].bottomAnchor.constraintEqualToAnchor(guides[1].topAnchor),
    views[2].bottomAnchor.constraintEqualToAnchor(guides[2].topAnchor),
    // top of each view is bottom of preceding guide
    views[1].topAnchor.constraintEqualToAnchor(guides[0].bottomAnchor),
    views[2].topAnchor.constraintEqualToAnchor(guides[1].bottomAnchor),
    views[3].topAnchor.constraintEqualToAnchor(guides[2].bottomAnchor)
])

(Очевидно, я мог бы сделать этот код cuter и less с использованием циклов, но я намеренно развернул петли для ясности, чтобы вы могли видеть шаблон и технику.)