Когда пользовательский интерфейс сложнее того, что мы создали в примере, то количество создаваемых связей, а соответственно и кода, становится в разы больше. Создание связей в коде и их дальнейшая отладка становится трудоемкой и утомительной задачей.
Создание связей значительно упрощает визуальный формат для описания связей (Visual Format Language
, VFL
). Визуальный формат определяет синтаксис - последовательность ASCII символов — с помощью которого могут быть созданы AutoLayout
связи. Синтаксис VFL
примечателен тем, что используемые в нем ASCII символы визуально представляют собой создаваемые связи.
Однако стоит отметить, что сейчас с помощью VFL
можно описать не все связи. Возможно, что в будущем VFL
будет расширен, и мы сможем с помощью его описывать все связи.
Рассмотрим несколько примеров для лучшего понимания того, что из себя представляет VFL
.
Первый пример создает связь которая говорит, что левая сторона кнопки button2
должна находится правее левой стороны кнопки button1
, на расстоянии, используемом по умолчанию:
[button1]-[button2]
Следующий пример также создает связь между двумя кнопками, но расстояние между кнопками будет в 30 пикселей:
[button1]-30-[button2]
Расстояние между представлениями обозначается символом «-». Создаваемые связи по умолчанию считаются горизонтальными. Для создания вертикальной связи перед ней необходимо добавить префикс «V:». Следующий пример демонстрирует создание вертикальной связи:
V:[button1]-30-[button2]
Также существует и префикс «H:», который может быть использован при описании связи. Описание связи:
[button1]-[button2]
эквивалентно:
H:[button1]-[button2]
Родительское представление обозначается символом «|»:
|-30-[textField]-30-|
В это примере мы создали связь, которая говорит, что левая сторона текстового поля должна быть закреплена на расстоянии 30 пикселей от левой стороны родительского представления, а правая сторона текстового поля должна быть закреплена на расстоянии 30 пикселей от правой стороны родительского представления.
Мы также можем обозначить размер связи, размеры задаются в круглых скобках «()»:
[button1(100)]
В данном примере мы создали связь, которая говорит, что размер кнопки button1
должен быть 100 пикселей.
В следующем примере мы создадим связь, которая говорит, что ширина кнопки button1
должна быть 100 пикселей, а ширина кнопки button2
должна быть равна ширине кнопки button1
. Также кнопки должны находится на расстоянии в 20 пикселей друг от друга:
[button1(100)]-20-[button2(==button1)]
В таблице приведены допустимые ASCII символы и их описание:
Символ | Описание | Пример |
---|---|---|
[view] | Обозначает представление | [view1] |
| | Обозначает родительскоепредставление | |-20-[view]-20-| |
- | Обозначает стандартное расстояние | [view1]-[view2] |
-N- | Обозначает расстояние в N пикселей | [view1]-40-[view2] |
H: | Обозначает горизонтальную связь | H:[view1]-30-[view2] |
V: | Обозначает вертикальную связь | V: [view1]-50-[view2] |
>=, <=, = = | Обозначает отношение, может быть использован только с размерами связи | [view1(= =view2)] |
@N | Обозначает приоритет, может бытьиспользован только с размерами связи. По умолчанию приоритет 1000. | [view(= =50@500)] |
Создание связей описанных с помощью VLF осуществляется с помощью метода класса:
+(NSArray *)constraintsWithVisualFormat:(NSString *)format options:(NSLayoutFormatOptions)opts metrics:(NSDictionary *)metrics views:(NSDictionary *)views
Метод возвращает массив связей, которые должны быть добавлены к родительскому представлению.
Аргументы метода:
format
- строка с описанием связи(ей) наVFL
options
- битовая маска с опциями задающими направление расположения и атрибуты для всех элементовmetrics
- ассоциативный массив с метриками илиnil
. В массиве допустимы только объекты NSNumber.views
- ассоциативный массив (словарь) представлений для которых создаются с вязи
Чтобы лучше понимать назначение аргументов, посмотрим на следующий пример:
[buttonOne]-20-[buttonTwo]
В этом примере мы создали связь между кнопками buttonOne
и buttonTwo
. buttonOne
и buttonTwo
- это ключи элементов словаря views:
UIButton *buttonOne = [UIButton buttonWithType:UIButtonTypeRoundedRect];
UIButton *buttonTwo = [UIButton buttonWithType:UIButtonTypeRoundedRect];
....
NSDictionary *views = @{@"buttonOne": buttonOne, @"buttonTwo": buttonTwo};
В следующем примере вместо явного указания расстояния между кнопками мы используем метрики:
[buttonOne]-space-[buttonTwo]-space-[buttonThree]
В данном случае space - это ключ элемента словаря metrics:
NSNumber *space= [NSNumber numberWithFloat:20.0];
NSDictionary *metrics = @{@"space": space};
Создать словарь views
и metrics
также можно воспользовавшись макросом NSDictionaryOfVariableBindings
:
NSDictionary *views = NSDictionaryOfVariableBindings(buttonOne, buttonTwo);
NSDictionary *metrics = NSDictionaryOfVariableBindings(space);
это эквивалентно
NSDictionary *views = @{@"buttonOne": buttonOne, @"buttonTwo": buttonTwo};
NSDictionary *metrics = @{@"space": space};
В качестве ключей словаря будут использованы названия указанных переменных.
В прошлой части мы создавали связи между тремя кнопками, сейчас рассмотрим создание этих же связей с помощью VFL
. Создадим в XCode
4.5 «Single View» проект с названием «AutoLayoutCodeVFL»:
В методе viewDidLoad
, как и в примере из прошлого раздела, создаем три кнопки и размещаем их на родительском представлении:
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];
После чего создадим с помощью VFL связи и добавим их к родительскому представлению:
NSDictionary *views = NSDictionaryOfVariableBindings(buttonOne, buttonTwo, buttonThree);
NSNumber *space= [NSNumber numberWithFloat:10.0];
NSDictionary *metrics = NSDictionaryOfVariableBindings(space);
NSString *format = @"|-[buttonOne]-(<=space)-[buttonTwo(==buttonOne)]-(<=space)-[buttonThree(==buttonTwo)]-|";
NSMutableArray *constraints = [[NSMutableArray alloc] init];
[constraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:format options: NSLayoutFormatDirectionLeadingToTrailing metrics:metrics views:viewsDictionary]];
В приведенном выше коде мы первым делом создали два словаря views
и metrics
. Первый словарь содержит наши представления - кнопки. Второй - метрики. Затем с помощью VFL
мы описали следующие горизонтальные связи:
- связь, которая говорит, что левая сторона кнопки
buttonOne
должна быть закреплена на расстоянии, заданном по умолчанию от левой стороны родительского представления; - связь, которая говорит, что правая сторона кнопки
buttonThree
должна быть закреплена на расстоянии, заданном по умолчанию от правой стороны родительского представления; - связь, которая говорит, что между кнопкой
buttonOne
иbuttonTwo
должно быть расстояние, меньшее или равное расстоянию, заданному метрикой space; - связь, которая говорит, что между кнопкой
buttonTwo
иbuttonThree
должно быть расстояние, меньшее или равное расстоянию, заданному метрикой space; - связь, которая говорит, что ширина кнопки
buttonTwo
должна быть такой же, как и ширина кнопкиbuttonOne
; - связь, которая говорит, что ширина кнопки
buttonThree
должна быть такой же, как и ширина кнопкиbuttonTwo
.
Как нетрудно догадаться, с помощью VFL
мы описали связи, которые в прошлой части мы пометили, как pinToLeft
pinToRight
, >horizontalSpace1
, horizontalSpace2
, width1
и width2
.
Следующим шагом с помощью метод класса constraintsWithVisualFormat
: мы создали связи, которые сохранили в изменяемый массив constraints
.
Как было сказано ранее, с помощью VFL
мы можем создать не все связи, часть связей нам придется создать вручную:
NSLayoutConstraint *center = [NSLayoutConstraint constraintWithItem: buttonTwo
attribute: NSLayoutAttributeCenterX
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeCenterX
multiplier: 1.0f
constant: 0.0f
];
NSLayoutConstraint *vertical1 = [NSLayoutConstraint constraintWithItem: buttonOne
attribute: NSLayoutAttributeBottom
relatedBy: NSLayoutRelationEqual
toItem: self.view
attribute: NSLayoutAttributeBottom
multiplier: 1.0f
constant: -20.0f
];
NSLayoutConstraint *vertical2 = [NSLayoutConstraint constraintWithItem: buttonTwo
ttribute: 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
];
Добавляем созданные связи в массив constraints
:
[constraints addObject:center];
[constraints addObject:vertical1];
[constraints addObject:vertical2];
[constraints addObject:vertical3];
Осталось только все созданные связи добавить к родительскому представлению:
[self.view addConstraints:constraints];
запустить и посмотреть, что получилось