Criação de restrições de layout de maneira programática

84

Eu sei que muitas pessoas já me perguntaram toneladas sobre isso, mas mesmo com as respostas não consigo fazer funcionar.

Quando estou lidando com restrições no storyboard, é fácil, mas no código eu tenho dificuldade. Procuro, por exemplo, ter uma visão que fique do lado direito e tenha a altura da tela de acordo com a orientação da tela. Este é o meu código:

UIView *myView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 200, 748)];
myView.backgroundColor = [UIColor redColor];
[self.view addSubview:myView];
[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
    options:0 metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

Não satisfaz algumas restrições. Eu não vejo o que está errado. Além disso, por que não posso usar uma propriedade como em self.myViewvez de uma variável local como myView?

pierre23
fonte
O que é vivi no código acima?
iDev de
14
Altere o título para "iOS" em vez de "IOS" - o último se refere ao sistema operacional do switch / roteador da Cisco. Me confundiu. Obrigado.
armani
1
Várias perguntas sobre isso: (1) Que erro você está obtendo quando "não satisfaz algumas restrições"? (2) Você está traduzindo o autoresizing para restrições em myView? (3) O que é vivi(como perguntou ACB)? (4) Você tem uma propriedade myViewdeclarada self?
Tim,
1
ih Tim, desculpe vivi, toquei muito rápido ... é myView. O que você quer dizer com traduzir? Tudo o que faço é no código acima. Sobre a propriedade, eu tenho algumas outras visualizações que são declaradas como propriedade, mas quando eu implemento o mesmo código para essas propriedades, ele trava ... a mensagem "erro" é muito grande, não consigo colá-la aqui
pierre23
Para remover as restrições de uma determinada visualização temporariamente, faça isto stackoverflow.com/questions/13388104/…
Sam B

Respostas:

106

Ao usar Layout automático no código, a configuração do quadro não faz nada. Portanto, o fato de você ter especificado uma largura de 200 na vista acima não significa nada quando você define restrições nela. Para que o conjunto de restrições de uma visualização não seja ambíguo, são necessárias quatro coisas: uma posição x, uma posição y, uma largura e uma altura para qualquer estado dado.

Atualmente, no código acima, você tem apenas dois (altura, em relação à supervisualização e posição y, em relação à supervisualização). Além disso, você tem duas restrições necessárias que podem entrar em conflito, dependendo de como as restrições da visão geral da visualização são configuradas. Se a supervisualização tivesse uma restrição necessária que especifica que sua altura seja um valor menor que 748, você receberá uma exceção de "restrições insatisfatórias".

O fato de você definir a largura da visualização antes de definir as restrições não significa nada. Ele nem mesmo levará em consideração o quadro antigo e calculará um novo quadro com base em todas as restrições que especificou para essas visualizações. Ao lidar com autolayout no código, normalmente apenas crio uma nova visualização usando initWithFrame:CGRectZeroou simplesmente init.

Para criar o conjunto de restrições necessário para o layout que você descreveu verbalmente em sua pergunta, você precisaria adicionar algumas restrições horizontais para limitar a largura e a posição x, a fim de fornecer um layout totalmente especificado:

[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"V:|-[myView(>=748)]-|"
    options:NSLayoutFormatDirectionLeadingToTrailing
    metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

[self.view addConstraints:[NSLayoutConstraint
    constraintsWithVisualFormat:@"H:[myView(==200)]-|"
    options:NSLayoutFormatDirectionLeadingToTrailing
    metrics:nil
    views:NSDictionaryOfVariableBindings(myView)]];

A descrição verbal desse layout é a seguinte, começando com a restrição vertical:

myView preencherá a altura de seu superview com um preenchimento superior e inferior igual ao espaço padrão. O superview do myView tem uma altura mínima de 748 pontos. A largura de myView é 200pts e tem um preenchimento à direita igual ao espaço padrão contra sua visão panorâmica.

Se você simplesmente quiser que a visualização preencha toda a altura da supervisualização sem restringir a altura da supervisualização, então você simplesmente omite o (>=748)parâmetro no texto de formato visual. Se você acha que o (>=748)parâmetro é necessário para dar a ele uma altura - você não acha neste caso: fixando a visualização nas bordas da supervisualização usando a sintaxe de barra ( |) ou barra com espaço ( |-, -|), você está atribuindo um y à sua visualização -position (fixando a vista em uma única aresta), e uma posição y com altura (fixando a vista em ambas as arestas), satisfazendo assim seu conjunto de restrições para a vista.

Em relação à sua segunda pergunta:

Usando NSDictionaryOfVariableBindings(self.myView)(se você tiver uma configuração de propriedade para myView) e alimentando-a em seu VFL para usar self.myViewem seu texto VFL, você provavelmente obterá uma exceção quando o autolayout tentar analisar seu texto VFL. Tem a ver com a notação de ponto nas chaves do dicionário e com o sistema que tenta usar valueForKeyPath:. Veja aqui uma pergunta e resposta semelhantes .

Larsacus
fonte
Não se esqueça de que alguns objetos como UILabel têm um tamanho intrínseco e você não precisa definir a largura ou altura desse objeto, apenas sua posição. No entanto, se você definir a altura ou a largura, acredito que removerá ambos e você terá que fornecer ambos.
Nick Turner
Mesmo que essa resposta não tenha respondido minha pergunta, ela me ajudou a perceber algo. Obrigado por ser! :)
Matej
1
A única introdução concisa e legível de layout automático na Internet até agora.
Joey Carson
por favor me ajude, minha pergunta é esta ... eu não sei como usar restrições de forma programática stackoverflow.com/questions/36326288/…
61

Oi, tenho usado muito esta página para restrições e "como fazer". Levei uma eternidade para chegar ao ponto de perceber que precisava:

myView.translatesAutoresizingMaskIntoConstraints = NO;

para fazer este exemplo funcionar. Obrigado Userxxx, Rob M. e especialmente larsacus pela explicação e código aqui, foi inestimável.

Aqui está o código completo para que os exemplos acima sejam executados:

UIView *myView = [[UIView alloc] init];
myView.backgroundColor = [UIColor redColor];
myView.translatesAutoresizingMaskIntoConstraints = NO;  //This part hung me up 
[self.view addSubview:myView];
//needed to make smaller for iPhone 4 dev here, so >=200 instead of 748
[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"V:|-[myView(>=200)]-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(myView)]];

[self.view addConstraints:[NSLayoutConstraint
                           constraintsWithVisualFormat:@"H:[myView(==200)]-|"
                           options:NSLayoutFormatDirectionLeadingToTrailing
                           metrics:nil
                           views:NSDictionaryOfVariableBindings(myView)]];
BriOnH
fonte
11

Versão rápida

Atualizado para Swift 3

Este exemplo mostrará dois métodos para adicionar programaticamente as seguintes restrições, da mesma forma que faria no Interface Builder:

Largura e altura

insira a descrição da imagem aqui

Centro no recipiente

insira a descrição da imagem aqui

Código padrão

override func viewDidLoad() {
    super.viewDidLoad()

    // set up the view
    let myView = UIView()
    myView.backgroundColor = UIColor.blue
    myView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(myView)

    // Add constraints code here (choose one of the methods below)
    // ...
}

Método 1: estilo âncora

// width and height
myView.widthAnchor.constraint(equalToConstant: 200).isActive = true
myView.heightAnchor.constraint(equalToConstant: 100).isActive = true

// center in container
myView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
myView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true

Método 2: Estilo NSLayoutConstraint

// width and height
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.width, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 200).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.height, relatedBy: NSLayoutRelation.equal, toItem: nil, attribute: NSLayoutAttribute.notAnAttribute, multiplier: 1, constant: 100).isActive = true

// center in container
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerX, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerX, multiplier: 1, constant: 0).isActive = true
NSLayoutConstraint(item: myView, attribute: NSLayoutAttribute.centerY, relatedBy: NSLayoutRelation.equal, toItem: view, attribute: NSLayoutAttribute.centerY, multiplier: 1, constant: 0).isActive = true

Notas

  • O estilo âncora é o método preferido em vez do NSLayoutConstraintestilo, no entanto, está disponível apenas no iOS 9, portanto, se você oferece suporte ao iOS 8, ainda deve usar o NSLayoutConstraintestilo.
  • Consulte também a documentação Criando restrições de maneira programática .
  • Veja esta resposta para um exemplo semelhante de adição de uma restrição de fixação.
Suragch
fonte
5

Com relação à sua segunda pergunta sobre propriedades, você pode usar self.myViewapenas se você declarou como uma propriedade em classe. Como myViewé uma variável local, você não pode usá-la dessa forma. Para obter mais detalhes sobre isso, eu recomendo que você leia a documentação da apple em Propriedades Declaradas ,

iDev
fonte
5

por que não posso usar uma propriedade como em self.myViewvez de uma variável local como myView?

tente usar:

NSDictionaryOfVariableBindings(_view)

ao invés de self.view

Megarushing
fonte
Isso fez meu aplicativo travar, ele deu a mensagem: 'Incapaz de analisar o formato de restrição: polylineImageView não é uma chave no dicionário de visualizações.'
Tai Le
4

Observe também que, no iOS9, podemos definir restrições programaticamente "mais concisas e mais fáceis de ler" usando subclasses da nova classe auxiliar NSLayoutAnchor .

Um exemplo do doc:

[self.cancelButton.leadingAnchor constraintEqualToAnchor:self.saveButton.trailingAnchor constant: 8.0].active = true;
andreacipriani
fonte