Centralizando uma visualização em sua superview usando Visual Format Language

160

Comecei a aprender o AutoLayout para iOS e dei uma olhada no Visual Format Language.

Tudo funciona bem, exceto por uma coisa: eu simplesmente não consigo ter uma visão centralizada na sua visão geral.
Isso é possível com o VFL ou preciso criar manualmente uma restrição?

Christian Schnorr
fonte

Respostas:

232

Atualmente, não, não parece possível centralizar uma visualização na superview usando apenas VFL. No entanto, não é tão difícil fazê-lo usando uma única string VFL e uma única restrição extra (por eixo):

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

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

Alguém poderia pensar que você seria capaz de fazer isso (que é o que eu inicialmente pensei e tentei quando vi essa pergunta):

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

Tentei muitas variações diferentes das anteriores, tentando dobrá-las à minha vontade, mas isso não parece se aplicar à superview, mesmo quando há explicitamente duas seqüências de caracteres VFL separadas para os dois eixos ( H:|V:). Comecei a tentar isolar exatamente quando as opções não são aplicadas para o VFL. Eles parecem não se aplicar à superview no VFL e somente se aplicam a quaisquer visualizações explícitas mencionadas na string VFL (o que é decepcionante em certos casos).

Espero que, no futuro, a Apple adicione algum tipo de nova opção para que as opções do VFL levem em conta a superview, mesmo que apenas quando houver apenas uma única exibição explícita além da superview no VFL. Outra solução poderia ser outra opção passado para o VFL que diz algo como: NSLayoutFormatOptionIncludeSuperview.

Desnecessário dizer que aprendi muito sobre o VFL tentando responder a essa pergunta.

Larsacus
fonte
2
Obrigado por essa resposta detalhada. A única razão para eu usar o VFL, no entanto, é me livrar desses feios pedaços de restrições. Pena que a Apple não suporta este ainda ...
Christian Schnorr
@larsacus Você poderia comentar por que a resposta abaixo funciona? Isso me deixou perplexo.
Matt G
8
A resposta de Evgeny abaixo funciona porque o mecanismo de autolayout trata |e [superview]como entidades internas separadas, embora representem o mesmo objeto semântico. Ao usar [superview], o autolayout inclui o objeto superview em suas métricas de alinhamento (no caso de sua resposta no link do github, é a NSLayoutFormatAlignAllCenterY/ Xmétrica).
Larsacus
@larsacus Awesome, Obrigado!
Matt G
Sabemos se a limitação ainda se aplica à versão do VFL que acompanha o iOS 7.x atual?
Dr
138

Sim, é possível centralizar uma visualização em sua superview com Visual Format Language. Vertical e horizontalmente. Aqui está a demonstração:

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

Evgenii
fonte
15
Isso é incrível, você acha que poderia expandir isso e explicar por que funciona?
tapi
3
Acabei utilizando algo mais perto da resposta marcada, mas um de um anexo de github em seu
Rembrandt Q. Einstein
4
@ Dan Se isso não funcionar para você, também pode ser necessário ter restrições de largura e altura. O VFL seria assim para largura: @ "[etiqueta (== 200)]" e assim por altura: @ "V: [etiqueta (== 200)]"
Jonathan Zhan
1
Por que os resultados são de | e [superview] diferente? Isso é algo que deveria ser transmitido por radar ou está acontecendo que eu simplesmente não estou seguindo?
Matt G
3
Não é possível analisar o formato da restrição: as opções mascaram as vistas necessárias para serem alinhadas em uma aresta horizontal, o que não é permitido para o layout também horizontal. H: [superview] - (<= 1) - [label1]
grabner 10/10
82

Eu acho que é melhor criar manualmente restrições.

[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]];

Agora podemos usar o NSLayoutAnchor para definir programaticamente o AutoLayout:

(Disponível no iOS 9.0 e posterior)

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

Eu recomendo usar o SnapKit , que é um DSL para facilitar o Auto Layout no iOS e no macOS.

view.snp.makeConstraints({ $0.center.equalToSuperview() })
jqgsninimo
fonte
1
Funcionou melhor no meu contexto - sem VFL.
brainray
dá-me um aviso: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.
Tapas Pal
10

Absolutamente possível foi capaz de fazê-lo assim:

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

Aprendi isso daqui. O código acima centraliza a subvisão à vista de Y, obviamente.

Swift3:

        NSLayoutConstraint.constraints(withVisualFormat: "H:[view]-(<=1)-[subview]",
                                       options: .alignAllCenterY,
                                                       metrics: nil,
                                                       views: ["view":self, "subview":_spinnerView])
Igor Muzyka
fonte
7

Adicione o código abaixo e coloque seu botão no centro da tela. Absolutamente possível.

[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]];
Pankaj Gaikar
fonte
1

Eu sei que não é o que você deseja, mas é claro que você pode calcular as margens e usá-las para criar a string de formato visual;)

Enfim, não. Infelizmente, não é possível fazer isso 'automaticamente' com o VFL - pelo menos ainda não.

Tobi
fonte
Bem, você pode usar o NSString stringWithFormat: para criar sua string VFL dinamicamente em tempo de execução.
TRVD1707
1

Graças à resposta do @Evgenii, crio um exemplo completo no gist:

centralize o quadrado vermelho verticalmente com altura personalizada

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

Você pode centralizar uma vista verticalmente usando VFL (que não é muito intuitivo) ou usar restrições manualmente. A propósito, não posso combinar o centerY e a altura no VFL, como:

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

Na solução atual, as restrições de VFL são adicionadas separadamente.

Allen Huang
fonte
0

O que é necessário fazer é que a superview seja declarada no dicionário. Em vez de usar |você usa@{@"super": view.superview}; .

E NSLayoutFormatAlignAllCenterXpara vertical e NSLayoutFormatAlignAllCenterYhorizontal como opções.

lagos
fonte
0

Você pode usar visualizações extras.

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]];
 }];

Se você quiser arrumado, então centerXView.hidden = centerYView.hidden = YES;.

PS: Não sei se posso chamar as visualizações extras de "espaços reservados", porque o inglês é meu segundo idioma. Será apreciado se alguém puder me dizer isso.

CopperCash
fonte
Algo está faltando aqui. Como você chama o bloco sob a declaração de visualizações ? Não é um código legal da maneira que você coloca
ishahak
0

Para aqueles que vieram aqui em busca de uma Interface Buildersolução baseada (o Google me leva aqui), basta adicionar restrições de largura / altura const e selecione a subview -> control arraste para sua superview -> selecione "center vertical / horizontal in superview".

superarts.org
fonte
0

Este é o meu método

//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
fonte
-1

Só preciso dizer que você pode usar isso em vez de restrições:

child.center = [parent convertPoint:parent.center fromView:parent.superview];
Nik Kov
fonte
4
Só precisa dizer, isso não é o que ele está pedindo nem vai funcionar se você girar o dispositivo, de obter a tela redimensionada, etc.
jeffjv
-1

@ resposta igor-muzyka no Swift 2 :

NSLayoutConstraint.constraintsWithVisualFormat("H:[view]-(<=1)-[subview]", 
                                               options: .AlignAllCenterY,
                                               metrics: nil,
                                               views: ["view":view, "subview":subview])
Federico Zanetello
fonte
-3

Em vez de usar o VFL, você pode fazer isso facilmente no construtor de interfaces. No Storyboard principal, pressione a tecla Ctrl e clique na vista que você deseja centralizar. Arraste para a superview (mantendo pressionada a tecla ctrl) e selecione "Center X" ou "Center Y". Nenhum código é necessário.

leenyburger
fonte