Como posso definir restrições de proporção de aspecto de maneira programática no iOS?

85

Eu usei o layout automático para meus controladores de visualização. Eu defini as posições V e H nas restrições, mas quero saber como posso aumentar o tamanho do meu botão quando ele muda para 5s, 6 e 6 Plus. Esta é a maneira como adicionei restrições para o botão de login:

NSArray *btncon_V=[NSLayoutConstraint constraintsWithVisualFormat:@"V:[btnLogin(40)]" options:0 metrics:nil views:viewsDictionary];
[btnLogin addConstraints:btncon_V];

NSArray *btncon_POS_H=[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-100-[btnLogin]-100-|" options:0 metrics:nil views:viewsDictionary];
[self.view addConstraints:btncon_POS_H];


NSArray *btncon_POS_V=[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-70-[Title]-130-[lblFirst]-0-[lblSecond]-20-[textusername]-10-[txtpassword]-10-[btnLogin]" options:0 metrics:nil views:viewsDictionary];

[self.view addConstraints:btncon_POS_V];

Mas meu problema é que, embora ele gerencie a lacuna entre os lados esquerdo e direito, está ficando esticado no iPhone 6 e 6 Plus, já que a altura é fixa. Como posso aumentar o tamanho de acordo com o tamanho da tela? Acho que essa pode ser a proporção da imagem, mas como posso definir a restrição da proporção no código?

IRD
fonte
2
Verifique esta resposta: stackoverflow.com/a/12526630/3202193
Ashish Kakkad

Respostas:

84

Como isso. Tente uma vez.

[self.yourview setTranslatesAutoresizingMaskIntoConstraints:NO];
[self.yourview addConstraint:[NSLayoutConstraint
                                  constraintWithItem:self.yourview
                                  attribute:NSLayoutAttributeHeight
                                  relatedBy:NSLayoutRelationEqual
                                  toItem:self.yourview
                                  attribute:NSLayoutAttributeWidth
                                  multiplier:(self.yourview.frame.size.height / self.yourview.frame.size.width)
                                  constant:0]];

ou no lugar de (self.yourview.frame.size.height / self.yourview.frame.size.width)você pode usar qualquer valor float. Obrigado.

Swift 3.0 -

self.yourview!.translatesAutoresizingMaskIntoConstraints = false
self.yourview!.addConstraint(NSLayoutConstraint(item: self.yourview!,
                                          attribute: NSLayoutAttribute.height,
                                          relatedBy: NSLayoutRelation.equal,
                                          toItem: self.yourview!,
                                          attribute: NSLayoutAttribute.width,
                                          multiplier: self.yourview.frame.size.height / self.yourview.frame.size.width,
                                          constant: 0))
Mahesh Agrawal
fonte
Ele está me dando este erro Encerrando o aplicativo devido à exceção não capturada 'NSInternalInconsistencyException', motivo: 'O multiplicador não é finito! Isso é ilegal. multiplicador: nan '
IRD de
2
significa no cenário como se a altura fosse 100 e a largura fosse 200 e você fornecesse o multiplicador 0,5 com o meu código, então se sua altura aumentasse para 150 mudando qualquer outra restrição, a largura aumentaria automaticamente para 300. Mas para fazer isso você precisa para aumentar uma restrição da vista, como largura ou altura.
Mahesh Agrawal
1
terá altura: largura = 1: 2 que é 0,5.
Mahesh Agrawal
3
Não se esqueça de adicionar esta linha antes de definir quaisquer restrições programaticamente: [self.yourview setTranslatesAutoresizingMaskIntoConstraints:NO];
atulkhatri
1
Ao usar restrições, meus tamanhos de quadro eram todos zero. Em vez disso, para obter a proporção, useiself.yourview.intrinsicContentSize
Phlippie Bosman
89

Âncoras de layout é a maneira mais conveniente de definir restrições de maneira programática.

Digamos que você queira definir uma proporção de 5: 1 para seu botão, então você deve usar:

button.heightAnchor.constraint(equalTo: button.widthAnchor, multiplier: 1.0/5.0).isActive = true

Aqui está o código completo:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        let button = UIButton(type: .custom)
        button.setTitle("Login", for: .normal)
        button.backgroundColor = UIColor.darkGray

        self.view.addSubview(button)

        button.translatesAutoresizingMaskIntoConstraints = false

        let margins = view.layoutMarginsGuide

        button.leadingAnchor.constraint(equalTo: margins.leadingAnchor, constant: 20.0).isActive = true
        button.trailingAnchor.constraint(equalTo: margins.trailingAnchor, constant: -20.0).isActive = true
        button.bottomAnchor.constraint(equalTo: margins.bottomAnchor, constant: -20.0).isActive = true
        button.heightAnchor.constraint(equalTo: button.widthAnchor, multiplier: 1.0/5.0).isActive = true
    }

}

Aqui estão os resultados obtidos com o código escrito acima. Você pode ver que o botão mantém sua proporção de 5: 1 em vários dispositivos:

Visualização do resultado

Eugene Brusov
fonte
14

Swift 3:

yourView.addConstraint(NSLayoutConstraint(item: yourView,
                                          attribute: .height,
                                          relatedBy: .equal,
                                          toItem: yourView,
                                          attribute: .width,
                                          multiplier: 9.0 / 16.0,
                                          constant: 0))
RNHTTR
fonte
1
Para iOS 8 e superior, você deve usar a propriedade isActive de NSLayoutConstraint em vez de chamar addConstraint (:). aspectRatioConstraint.isActive = true
dvdblk
Por que você não usa .widthcomo o primeiro atributo? O resultado é que attr2 * multiplier a proporção é width / height, portanto width = height * aspectRatio, se colocarmos o .heightno primeiro atributo e o aspectRationo multiplier, o resultado será totalmente invertido.
Kimi Chiu
13

Você pode definir "Restrição de altura" em tempo de design no Interface Builder. Basta marcar '"Remover em tempo de compilação" e removerá quando o aplicativo estiver em execução.

insira a descrição da imagem aqui Depois disso, você pode adicionar a restrição "Aspect Ratio", por exemplo, no método viewDidLoad.

No Xamain.iOS para "16: 9" é assim:

    this.ImageOfTheDay.AddConstraint(NSLayoutConstraint.Create(
            this.ImageOfTheDay,
            NSLayoutAttribute.Height,
            NSLayoutRelation.Equal,
            this.ImageOfTheDay,
            NSLayoutAttribute.Width,
            9.0f / 16.0f,
            0));

Ou, como disse Mahesh Agrawal :

    [self.ImageOfTheDay addConstraint:[NSLayoutConstraint
                              constraintWithItem:self.ImageOfTheDay
                              attribute:NSLayoutAttributeHeight
                              relatedBy:NSLayoutRelationEqual
                              toItem:self.ImageOfTheDay
                              attribute:NSLayoutAttributeWidth
                              multiplier:(9.0f / 16.0f)
                              constant:0]];
Zanael
fonte
12

Extensão auxiliar no UIView

extension UIView {

    func aspectRation(_ ratio: CGFloat) -> NSLayoutConstraint {

        return NSLayoutConstraint(item: self, attribute: .height, relatedBy: .equal, toItem: self, attribute: .width, multiplier: ratio, constant: 0)
    }
}

E o uso é:

view.aspectRation(1.0/1.0).isActive = true

Michał Ziobro
fonte
e uso: view.aspectRation (1.0 / 1.0)
Michał Ziobro
1
Esta é de fato uma solução elegante, barrando alguns tipos: 1. O parâmetro toItem é definido como em .selfvez de self2. E o uso no comentário deve ser view.aspectRation(1.0/1.0).isActive = true Eu testei isso no Xcode 10.2.1 Swift 5.
Pankaj Kulkarni
3

Tentei todas as respostas acima, mas não funcionou, e esta é a minha solução com o swift 3:

let newConstraint = NSLayoutConstraint(item: yourView, attribute: .width, relatedBy: .equal, toItem: yourView, attribute: .height, multiplier: 560.0/315.0, constant: 0)
yourView.addConstraint(newConstraint)
NSLayoutConstraint.activate([newConstraint])  
NSLayoutConstraint.deactivate(yourView.constraints)
yourView.layoutIfNeeded()
tuandapen
fonte
Você deve sempre desativar as restrições (potencialmente conflitantes) antes de adicionar novas para evitar erros de layout automático
João
0

Para visualizações, existem restrições e também propriedades.

as proporções são uma propriedade da visualização , então você pode configurá-la usando o modo de conteúdo, assim

imageView.contentMode = .scaleAspectFit

isso fará com que uma visualização não mude as proporções se, por exemplo

  • a altura ou largura são maiores do que cabem na tela
  • a altura foi codificada e, portanto, não será capaz de se adaptar a diferentes tamanhos de telas.
valeriana
fonte
A questão não é sobre ImageView e seu modo de conteúdo, é sobre layout automático e restrições. No Interface Builder, você pode definir a restrição da proporção de aspecto que, no final, cria uma restrição de largura para altura com um multiplicador.
Gunhan,
@Gunhan, a questão está relacionada a como configurá-lo programaticamente. Portanto, ser capaz de configurá-lo no construtor de interface é bom saber, mas irrelevante para esta questão.
valeriana,
Acho que você entendeu mal o que eu disse antes. Nesta pergunta, ninguém está falando sobre ImageView. TI trata de layout e restrições automáticos, e deve torná-los programaticamente. O que eu disse foi que quando você o configurou no construtor de interface, ele cria uma restrição de largura para altura. Não falei para você criá-lo via IB. Para fazer a mesma coisa programaticamente, você deve criá-lo view.widthAnchor.constraint(equalTo: view.heightAnchor, multiplier: aspectRatio, constant: 0)assim
Gunhan,