antonio's blog

antonio's blog


Блог о всяком разном, связанном с разработкой ПО. Пишу редко, когда есть время и желание.

Anton Dobkin
Author

Share


Tags


AutoLayout в iOS. Часть 2

Anton DobkinAnton Dobkin

Создавать 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) в коде:

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

Теперь создадим последние связи with1 и 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: принимает массив объектов.

Запустим проект и посмотрим что получилось

Anton Dobkin
Author

Anton Dobkin

Comments