antonio's blog

antonio's blog


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

Anton Dobkin
Author

Share


Tags


AutoLayout в iOS. Часть 3

Anton DobkinAnton Dobkin

Когда пользовательский интерфейс сложнее того, что мы создали в примере, то количество создаваемых связей, а соответственно и кода, становится в разы больше. Создание связей в коде и их дальнейшая отладка становится трудоемкой и утомительной задачей.

Создание связей значительно упрощает визуальный формат для описания связей (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

Метод возвращает массив связей, которые должны быть добавлены к родительскому представлению.

Аргументы метода:

Чтобы лучше понимать назначение аргументов, посмотрим на следующий пример:

[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 мы описали следующие горизонтальные связи:

  1. связь, которая говорит, что левая сторона кнопки buttonOne должна быть закреплена на расстоянии, заданном по умолчанию от левой стороны родительского представления;
  2. связь, которая говорит, что правая сторона кнопки buttonThree должна быть закреплена на расстоянии, заданном по умолчанию от правой стороны родительского представления;
  3. связь, которая говорит, что между кнопкой buttonOne и buttonTwo должно быть расстояние, меньшее или равное расстоянию, заданному метрикой space;
  4. связь, которая говорит, что между кнопкой buttonTwo и buttonThree должно быть расстояние, меньшее или равное расстоянию, заданному метрикой space;
  5. связь, которая говорит, что ширина кнопки buttonTwo должна быть такой же, как и ширина кнопки buttonOne;
  6. связь, которая говорит, что ширина кнопки 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];

запустить и посмотреть, что получилось

Anton Dobkin
Author

Anton Dobkin

Comments