Центрирование представления в его суперпредставлении с использованием языка визуальных форматов

160

Я только начал изучать AutoLayout для iOS и взглянул на Visual Format Language.

Все работает отлично, за исключением одного: я просто не могу получить представление в центре его суперпредставления.
Возможно ли это с VFL или мне нужно вручную создать ограничение?

Кристиан Шнорр
источник

Ответы:

232

В настоящее время нет, не похоже, что можно центрировать вид в суперпредставлении, используя только VFL. Однако это не так сложно сделать, используя одну строку VFL и одно дополнительное ограничение (на ось):

VFL: "|-(>=20)-[view]-(>=20)-|"

[NSLayoutConstraint constraintWithItem:view
                             attribute:NSLayoutAttributeCenterX
                             relatedBy:NSLayoutRelationEqual
                                toItem:view.superview
                             attribute:NSLayoutAttributeCenterX
                            multiplier:1.f constant:0.f];

Можно подумать, что вы просто сможете сделать это (это то, что я изначально думал и пытался, когда я увидел этот вопрос):

[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=20)-[view(==200)]-(>=20)-|"
                                 options: NSLayoutFormatAlignAllCenterX | NSLayoutFormatAlignAllCenterY
                                 metrics:nil
                                   views:@{@"view" : view}];

Я пробовал много разных вариантов вышеописанного, пытаясь подчинить его своей воле, но, похоже, это не относится к суперпредставлению, даже если явно иметь две отдельные строки VFL для обеих осей ( H:|V:). Затем я начал , чтобы попытаться изолировать точно , когда варианты действительно получить применяется к ЛВЖ. По-видимому, они не применяются к суперпредставлению в VFL и будут применяться только к любым явным представлениям, упомянутым в строке VFL (что в некоторых случаях вызывает разочарование).

Я надеюсь, что в будущем Apple добавит какую-то новую опцию, чтобы опции VFL учитывали суперпредставление, даже если это делается только тогда, когда существует только одно явное представление помимо суперпредставления в VFL. Другим решением может быть другой вариант принят в VFL , что говорит что - то вроде: NSLayoutFormatOptionIncludeSuperview.

Излишне говорить, что я многое узнал о VFL, пытаясь ответить на этот вопрос.

larsacus
источник
2
Спасибо за этот подробный ответ. Однако единственная причина, по которой я могу использовать VFL, - избавиться от этих отвратительных кусков ограничений. Жаль, что Apple пока не поддерживает это ...
Кристиан Шнорр,
@larsacus Не могли бы вы прокомментировать, почему работает ответ ниже? Это поставило меня в тупик.
Matt G
8
Ответ Евгения, приведенный ниже, работает, потому что механизм автоматического размещения обрабатывает |и [superview]как отдельные внутренние объекты, даже если они представляют один и тот же семантический объект. При использовании [superview]autolayout объект superview включается в его метрику выравнивания (в случае, когда он отвечает по ссылке на github, это метрика NSLayoutFormatAlignAllCenterY/ X).
Ларсак
@larsacus Круто, спасибо!
Matt G
Знаем ли мы, применимо ли это ограничение к версии VFL, которая поставляется с текущей версией iOS 7.x?
Друкс
138

Да, можно центрировать представление в его суперпредставлении с помощью языка визуальных форматов. Как вертикально, так и горизонтально. Вот демо:

https://github.com/evgenyneu/center-vfl

Евгений
источник
15
Это здорово, как вы думаете, вы могли бы расширить это и объяснить, почему это работает?
Тапи
3
Я закончил тем, что использовал что-то ближе к помеченному ответу, но +1 для вложения github в вашем
Рембрандт Q. Эйнштейн
4
@Dan Если это не сработает для вас, возможно, вам понадобятся ограничения для ширины и высоты. VFL будет таким для ширины: @ "[label (== 200)]" и таким же для высоты: @ "V: [label (== 200)]"
Джонатан Жан
1
Почему результаты | а [супервью] разные? Это что-то, что должно быть радарным или там происходит, что я просто не следую?
Matt G
3
Невозможно проанализировать формат ограничения: параметры маскируют требуемые виды, которые должны быть выровнены по горизонтальному краю, что недопустимо для макета, который также является горизонтальным. H: [superview] - (<= 1) - [label1]
grabner
82

Я думаю, что лучше вручную создавать ограничения.

[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterX
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterX
                             multiplier:1
                               constant:0]];
[superview addConstraint:
 [NSLayoutConstraint constraintWithItem:view
                              attribute:NSLayoutAttributeCenterY
                              relatedBy:NSLayoutRelationEqual
                                 toItem:superview
                              attribute:NSLayoutAttributeCenterY
                             multiplier:1
                               constant:0]];

Теперь мы можем использовать NSLayoutAnchor для программного определения AutoLayout:

(Доступно в iOS 9.0 и более поздних версиях)

view.centerXAnchor.constraint(equalTo: superview.centerXAnchor).isActive = true
view.centerYAnchor.constraint(equalTo: superview.centerYAnchor).isActive = true

Я рекомендую использовать SnapKit , который является DSL, чтобы упростить автоматическую компоновку на iOS и MacOS.

view.snp.makeConstraints({ $0.center.equalToSuperview() })
jqgsninimo
источник
1
Лучше всего сработало в моем контексте - хотя VFL нет.
мозговой
это дает мне предупреждение:Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view. We're considering the collapse unintentional and using standard height instead.
Тапас Пал
10

Абсолютно возможно было сделать это так:

[NSLayoutConstraint constraintsWithVisualFormat:@"H:[view]-(<=1)-[subview]"
                                        options:NSLayoutFormatAlignAllCenterY
                                        metrics:nil
                                          views:NSDictionaryOfVariableBindings(view, subview)];

Узнал это отсюда. Код выше центов subview в виду Y, очевидно.

Swift3:

        NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=1)-[subview]",
                                       options: .alignAllCenterY,
                                                       metrics: nil,
                                                       views: ["view":self, "subview":_spinnerView])
Игорь Музыка
источник
7

Добавьте приведенный ниже код и поместите свою кнопку в центр экрана. Абсолютно возможно.

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterY
                           metrics:0
                           views:views]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[button]-|"
                           options:NSLayoutFormatAlignAllCenterX
                           metrics:0
                           views:views]];
Панкай Гайкар
источник
1

Я знаю, что вы этого не хотите, но вы, конечно, можете рассчитать поля и использовать их для создания строки визуального формата;)

Во всяком случае нет. К сожалению, это невозможно сделать «автоматически» с VFL - по крайней мере, пока.

Tobi
источник
Ну, вы можете использовать NSString stringWithFormat: для динамического построения строки VFL во время выполнения.
TRVD1707
1

Благодаря ответу @Evgenii я создаю полный пример в gist:

отцентрируйте красный квадрат вертикально по высоте

https://gist.github.com/yallenh/9fc2104b719742ae51384ed95dcaf626

Вы можете центрировать вид по вертикали, используя VFL (что не совсем интуитивно понятно) или используя ограничения вручную. Кстати, я не могу объединить центр и высоту вместе в VFL, как:

"H:[subView]-(<=1)-[followIcon(==customHeight)]"

В текущем решении ограничения VFL добавляются отдельно.

Аллен Хуан
источник
0

Что нужно сделать, так это то, что суперпредставление должно быть объявлено в словаре. Вместо использования |вы используете @{@"super": view.superview};.

И NSLayoutFormatAlignAllCenterXдля вертикальных и NSLayoutFormatAlignAllCenterYдля горизонтальных как варианты.

Лагос
источник
0

Вы можете использовать дополнительные виды.

NSDictionary *formats =
@{
  @"H:|[centerXView]|": @0,
  @"V:|[centerYView]|": @0,
  @"H:[centerYView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterY),
  @"V:[centerXView(0)]-(>=0)-[yourView]": @(NSLayoutFormatAlignAllCenterX),
};
NSDictionary *views = @{
  @"centerXView": centerXView, 
  @"centerYView": centerYView, 
  @"yourView": yourView,
};
 ^(NSString *format, NSNumber *options, BOOL *stop) {
     [superview addConstraints:
      [NSLayoutConstraint constraintsWithVisualFormat:format
                                              options:options.integerValue
                                              metrics:nil
                                                views:views]];
 }];

Если хотите аккуратнее, тогда centerXView.hidden = centerYView.hidden = YES;.

PS: я не уверен, что могу назвать дополнительные представления "заполнителями", потому что английский - мой второй язык. Будет оценено, если кто-то скажет мне это.

CopperCash
источник
Здесь чего-то не хватает. Как называется блок под просмотры декларации? Это не правовой кодекс, как вы это
выразили
0

Для тех, кто пришел сюда для Interface Builderрешения на основе (Google приводит меня здесь), просто добавьте постоянные ограничения ширины / высоты и выберите подпредставление -> элемент управления перетаскиванием на его суперпредставление -> выберите «центр вертикально / горизонтально в суперпредставлении».

superarts.org
источник
0

Это мой метод

//create a 100*100 rect in the center of superView
var consts = NSLayoutConstraint.constraints(withVisualFormat: "H:|-space-[myView]-space-|", options: [], metrics:["space":view.bounds.width/2-50], views:["myView":myView])

consts += NSLayoutConstraint.constraints(withVisualFormat: "V:|-space-[myView]-space-|", options: [], metrics: ["space":view.bounds.height/2-50], views: ["myView":myView])
Calcifer
источник
-1

Просто нужно сказать, что вы можете использовать это вместо ограничений:

child.center = [parent convertPoint:parent.center fromView:parent.superview];
Ник Ков
источник
4
Просто нужно сказать, что это не то, о чем он просит, и не будет работать, если вы поворачиваете устройство, размер экрана изменяется, и т. Д.
jeffjv
-1

@ igor-muzyka ответ в Swift 2 :

NSLayoutConstraint.constraintsWithVisualFormat("H:[view]-(<=1)-[subview]", 
                                               options: .AlignAllCenterY,
                                               metrics: nil,
                                               views: ["view":view, "subview":subview])
Федерико Занетелло
источник
-3

Вместо использования VFL вы можете легко сделать это в конструкторе интерфейсов. На главной раскадровке Ctrl + клик по представлению, которое вы хотите центрировать. Перетащите на суперпредставление (удерживая Ctrl) и выберите «Центр X» или «Центр Y». Код не нужен.

leenyburger
источник