Создавать AutoLayout связи между UI элементами можно не только в Interface Builder, это можно делать и программно
Рассмотрим создание связей в коде на простом примере. В первом примере создадим пользовательский интерфейс с тремя кнопками, как показано на картинке ниже.
Такой интерфейс мы уже создавали с помощью Interface Builder. Итак, приступим.
Создадим в XCode
4.5 новый "Single-View" проект с названием «AutoLayoutCode»:
Открываем файл ViewControlle.m и в методе viewDidLoad
создаем три кнопки с закругленными углами («Round Rect Button»):
UIButton *buttonOne = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonOne setTitle:@"Button 1" forState:UIControlStateNormal];
buttonOne.tag = 1;
buttonOne.translatesAutoresizingMaskIntoConstraints = NO;
UIButton *buttonTwo = [UIButton buttonWithType:UIButtonTypeRoundedRect];[buttonTwo setTitle:@"Button 2" forState:UIControlStateNormal];
buttonTwo.tag = 2;
buttonTwo.translatesAutoresizingMaskIntoConstraints = NO;
UIButton *buttonThree = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[buttonThree setTitle:@"Button 3" forState:UIControlStateNormal];
buttonThree.tag = 3;
buttonThree.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview: buttonOne];
[self.view addSubview: buttonTwo];
[self.view addSubview: buttonThree];
Обратите внимание, что у всех кнопок мы установили атрибут translatesAutoresizingMaskIntoConstraints
в значение NO
. По умолчанию, при добавлении представлений тулкит будет автоматически создавать связи, конвертируя autoresize маску.
После того, как мы создали кнопки (представления), и разместили их на главном (родительском) представлении, нам необходимо задать связи между ними. Но перед этим немого теории. Когда мы создавали связи в Interface Builder, за нас всю сложную работу делал XCode, нам не нужно было знать, как это работает. Теперь, когда мы создаем связи программно, нам необходимо понимать, как же все это работает.
Итак, каждая связь описывает отношения между двумя представлениями. Связь, создаваемая между представлениями, описывается уравнением:
view1.attr1 <relation> view2.attr2 * multiplier + constant
где view1 и view2 — представления между которыми устанавливается связь; attr1 и attr2 — атрибуты представлений view1 и view2, которые являются целями связи; <relation> - отношение между представления view1 и view2 (= =, <=, =>); multiplier и constant - переменные составляющие, используемые для изменения второго атрибута.
Уравнение может быть описано словами «первый атрибут, который расположен в левой части уравнения, должен быть равен (больше или меньше) измененному второму атрибуту, расположенному в правой части уравнения». То есть, связь — это отношение первого атрибута к измененному второму атрибуту.
В качестве примера посмотрим на связь, которая говорит, что ширина двух кнопок должна быть одинаковой. Она описывается следующим уравнением:
view1.width = = view2.width
В данном случае переменные составляющие уравнения multiplier и constant равны 1 и 0. Для краткости написания они в уравнении не указаны, так как не влияют на результат. Полная запись уравнения выглядела бы так:
view1.width = = view2.width * 1 + 0
В реальности связь — это ни что иное, как объект класса NSLayoutConstraint
. Атрибуты задаются константами из семейства NSLayoutAttribute
(например, NSLayoutAttributeRight
), а отношения — константами из семейства NSLayoutRelation
(например, NSLayoutRelationEqual
).
Существует два способа создания связи (объекта класса NSLayoutConstraint) в коде:
- с помощью визуального формата (
Visual Format Language
,VFL
); - непосредственно написание кода.
Понятно, что второй способ более трудоемкий, а использование VFL позволяет значительно упростить создание связей. Используя VFL
можно задать не все связи, поэтому иногда оба способа придется использовать совместно.
Создадим связи для наших элементов с использованием метода NSLayoutConstraint
класса:
+(id)constraintWithItem:(id)view1 attribute:(NSLayoutAttribute)attr1 relatedBy:(NSLayoutRelation)relation toItem:(id)view2 attribute:(NSLayoutAttribute)attr2 multiplier:(CGFloat)multiplier constant:(CGFloat)c
Метод создает связь между двумя представлениями view1 и view2. Метод возвращает экземпляр класса NSLayoutConstraint. Ниже изображены связи, которые нам необходимо создать:
Каждой связи на рисунке присвоено название — это названия переменных, которые мы будем использовать в коде.
Вначале создадим связь, обозначенную переменной vertical1
. Эта связь говорит, что нижняя сторона кнопки buttonOne
должна быть выше на 20 пунктов нижней стороны родительского представления, и она описывается уравнением:
view1.bottom = = view2.bottom * 1 - 20
В данном случае, view1 - это кнопка buttonOne; view2 - родительское представление self.view; атрибут bottom - константа NSLayoutAttributeBottom
; отношение = = - константа NSLayoutRelationEqual
Это уравнение может быть представлено следующим кодом:
NSLayoutConstraint *vertical1 = [NSLayoutConstraint constraintWithItem: buttonOne
attribute: NSLayoutAttributeBottom
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeBottom
multiplier: 1.0f
constant: -20.0f
];</code>
Связи vertical2 и verticl3 создаются аналогично:
<code>NSLayoutConstraint *vertical2 = [NSLayoutConstraint constraintWithItem: buttonTwo
attribute: NSLayoutAttributeBottom
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeBottom
multiplier: 1.0f
constant: -20.0f
];
NSLayoutConstraint *vertical3 = [NSLayoutConstraint constraintWithItem: buttonThree
attribute: NSLayoutAttributeBottom
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeBottom
multiplier: 1.0f
constant: -20.0f
];
Теперь создадим связь pinToLeft
. Данная связь говорит, что левая сторона кнопки buttonOne должна быть расположена на 20 пунктов правее левой стороны родительского представления:
view1.left = = view2.left * 1 + 20
view1 - это кнопка buttonOne; view2 - родительское представление self.view; атрибут left - константа NSLayoutAttributeLeading
; отношение = = - константа NSLayoutRelationEqual
Код:
NSLayoutConstraint *pinToLeft = [NSLayoutConstraint constraintWithItem: buttonOne
attribute: NSLayoutAttributeLeading
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeLeading
multiplier: 1.0f
constant: 20.0f
];
Таким же образом создаем связь pinToRight
, которая говорит, что правый край кнопки buttonThree
должен быть расположен на 20 пунктов левее правой стороны родительского представления:
NSLayoutConstraint *pinToRight = [NSLayoutConstraint constraintWithItem: buttonThree
attribute: NSLayoutAttributeTrailing
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeTrailing
multiplier: 1.0f
constant: -20.0f
];
Создаем связь center
, которая говорит, что центр кнопки bottomTwo
должен совпадать с центром родительского представления:
NSLayoutConstraint *center = [NSLayoutConstraint constraintWithItem: buttonTwo
attribute: NSLayoutAttributeCenterX
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeCenterX
multiplier: 1.0f
constant: 0.0f
];
Далее создаем связь horizontalSpace1
, которая говорит, что левая сторона кнопки buttonTwo
должна находиться правее на 10 пунктов или более правой стороны кнопки buttonOne
:
NSLayoutConstraint *horizontalSpace1 = [NSLayoutConstraint constraintWithItem: buttonTwo
attribute: NSLayoutAttributeLeading
relatedBy: NSLayoutRelationLessThanOrEqual
toItem: buttonOne
attribute: NSLayoutAttributeTrailing
multiplier: 1.0f
constant: 10.0f
];
и по аналогии horizontalSpace2
:
NSLayoutConstraint *horizontalSpace2 = [NSLayoutConstraint
constraintWithItem: buttonThree
attribute: NSLayoutAttributeLeading
relatedBy: NSLayoutRelationLessThanOrEqual
toItem: buttonTwo
attribute: NSLayoutAttributeTrailing
multiplier: 1.0f
constant: 10.0f
];
При создании связи horizontalSpace1
и horizontalSpace2
в качестве атрибутов были использованы константы NSLayoutAttributeLeading
и NSLayoutAttributeTrailing
вместо NSLayoutAttributeLeft
и NSLayoutAttributeRight
, которые также являются допустимыми атрибутами. Причина состоит в том, что Leading
и Trailing
адаптируются к направлению написания текста. При работе с локализацией, в которой текст пишется справа налево (например, иврит), Leading
станет правой стороной, а Trailing
— левой.
Теперь создадим последние связи with
1 и with2
, которые говорят, что ширина всех кнопок должна быть одинаковой:
NSLayoutConstraint *width1 = [NSLayoutConstraint constraintWithItem: buttonTwo
attribute: NSLayoutAttributeWidth
relatedBy: NSLayoutRelationEqual
toItem: buttonOne
attribute: NSLayoutAttributeWidth
multiplier: 1.0f
constant: 0.0f
];
NSLayoutConstraint *width2 = [NSLayoutConstraint constraintWithItem: buttonTwo
attribute: NSLayoutAttributeWidth
relatedBy: NSLayoutRelationEqual
toItem: buttonThree
attribute: NSLayoutAttributeWidth
multiplier: 1.0f
constant: 0.0f
];
После того, как были созданы все связи, их необходимо добавить к родительскому представлению. Связи добавляются с помощью методов addConstraint:
и addConstraints:
. Метод addConstraint:
в качестве аргумента принимает объект класса NSLayoutConstraint, а метод addConstraints:
принимает массив объектов:
[self.view addConstraints:@[vertical1, vertical2, vertical3, pinToLeft, pinToRight, center, horizontalSpace1, horizontalSpace2, width1, width2]];
Удалить связь можно с помощью метода removeConstraint:
и removeConstraints:
. Метод removeConstraint:
принимает объект класса NSLayoutConstraint
, который необходимо удалить, метод removeConstraints:
принимает массив объектов.
Запустим проект и посмотрим что получилось