Como usar "priorities" no autolayout com UIKit
Não é novidade que quando não definimos nossas prioridades as coisas começam a ficar um pouco bagunçadas não é mesmo?! E no desenvolvimento, não é diferente. Então, bora! Vamos falar de auto layout usando UIKit. Vou te mostrar um problema muito comum e uma forma de resolvê-lo. 🚨
Esse post consiste na seguinte estrutura:
- Por que eu preciso saber disso? 🤔
- O que é isso? 🆘
- E como se usa esse negócio? (Hands On) 🚀
- Me conta mais… 📌
Por que eu preciso saber disso? 🤔
Como sabemos, no mundo IOS temos o framework UIKit que nos fornece componentes para montar o nosso layout. Nesses componentes, muitas vezes (não é sempre) vêm tamanhos já definidos (width, height ou ambos), os quais chamamos de content intrisec size e que podem ser modificados de acordo com as constraints que adicionamos para eles ou até mesmo com a variação de seus conteúdos. Por exemplo, uma UILabel pode ter o mesmo tamanho do texto definido para ela, mas nada impede que seja modificada para ter um tamanho maior ao determinarmos uma regra.
Normalmente, há também uma adequação de layout, o componente foi criado para se adaptar de forma automática em casos de não definirmos regras para eles e podem crescer ou diminuir seu tamanho natural de acordo com a sua prioridade.
É isso mesmo, lidar com layout também é uma questão de prioridade pois para se adaptar está sempre se perguntando qual a view mais importante. Mas pensa comigo, se duas views são igualmente importantes, o que acontece? É meu caro, temos um conflito ou resultados visuais inesperados.
O que é isso? 🆘
UIKit define o grau de prioridade para constraints com valores de 0 a 1000. Duas características das views que indicam o quanto o tamanho delas pode sofrer alteração (ou seja, se esses valores são altos ou baixos) são contentHuggingPriority e contentCompressionResistancePriority.
De acordo com a documentação da Apple, contentHuggingPriority é o quanto uma view pode resistir para continuar com seu tamanho natural ao invés de aumentar. E já o contentCompressionResistancePriority é o quanto uma view pode resistir para continuar com seu tamanho natural ao invés de diminuir.
E como se usa esse negócio? (Hands On) 🚀
Agora que sabemos o que são essas propriedades, vamos usá-las a nosso favor! Quero trazer um exemplo de uma UIStackView que contém duas UILabels. Como vocês bem sabem, a prioridade das labels são iguais e em casos específicos, pode nos trazer um layout que não queremos.
Para implementar este exemplo, crie um novo projeto no Xcode e adicione uma stackview horizontal com duas labels na view controller:
import UIKit
class ViewController: UIViewController {
private lazy var contentStackView: UIStackView = {
let stack = UIStackView(arrangedSubviews: [descriptionLabel, valueLabel])
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .equalCentering
stack.spacing = 8
return stack
}()
private lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 12)
label.text = "Instituição:"
}()
private lazy var valueLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .boldSystemFont(ofSize: 12)
label.text = "KN Soluções"
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
private func setupView() {
view.backgroundColor = .white
view.addSubview(contentStackView)
contentStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
contentStackView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 24).isActive = true
contentStackView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -24).isActive = true
}
}
Centralizamos nossa stackview para melhor visualização. O resultado visual é o seguinte:
Até o momento, nada parece fora do normal, nossos textos são pequenos e a distribuição da nossa stackview se encarregou de separá-las sem que uma delas aumentasse além de seu conteúdo. Mas digamos que precisemos de uma descrição maior, onde precisaremos de uma quebra de texto como no exemplo abaixo:
private lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 12)
label.text = "Instituição (Nome abreviado da empresa a qual pertence ou pertenceu recentemente):"
label.lineBreakMode = .byWordWrapping
label.numberOfLines = .zero
return label
}()
O resultado de visual de nossa modificação será algo de difícil leitura:
🔍 Vamos investigar
Atualmente, ambas as labels tem prioridades iguais 250 em hugging priority e 750 em compression priority.
💡Você pode verificar esses valores usando: label.contentCompressionResistancePriority(for: .horizontal) e label.contentCompressionResistancePriority(for: .horizontal)
Não se preocupe, agora que sabemos sobre prioridades vai ficar bem mais fácil resolver este problema. Para nossa descrição iremos dizer que sua resistência em ficar menor é baixa, já que queremos que esse texto tenha seu tamanho natural. E para nosso valor, adicionaremos uma resistência em ficar maior bem alta, para evitar que seja comprimida.
☑️ Importante lembrar
Para usar essas propriedades precisamos definir duas coisas: em que eixo aplicaremos a resistência e o grau de prioridade que queremos que ela tenha. Como estamos tratando de sua largura, definiremos uma prioridade para seu eixo .horizontal e utilizaremos .defaultLow ou .defaultHigh para modificar suas prioridades para mais baixa ou mais alta, respectivamente.
private lazy var descriptionLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .systemFont(ofSize: 12)
label.text = "Instituição (Nome abreviado da empresa a qual pertence ou pertenceu recentemente):"
label.lineBreakMode = .byWordWrapping
label.numberOfLines = .zero
label.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
return label
}()
private lazy var valueLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = .boldSystemFont(ofSize: 12)
label.text = "KN Soluções"
label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
return label
}()
Prontinho! Agora sim temos o resultado que desejamos:
☑️ O que aconteceu com os valores de prioridade?
Nossa descriptionLabel que antes tinha sua compression priority com 750 agora passa a ter sua prioridade abaixada para 250. E nossa valuelabel que antes tinha sua hugging priority com 250 agora passa a ter sua prioridade aumentada para 750.
💡 Em muitos casos, modificar prioridade em apenas um dos objetos já traz o resultado desejado, por questões de didática e medidas preventivas utilizamos em ambos.
Tendo em vista que já falei demais, deixo aqui este resumo para complementar nosso hands on:
setContentHuggingPriority — quanto mais alta, maior resistência para crescer
setContentCompressionResistancePriority — quanto mais alta, maior resistência para encolher
Me conta mais… 📌
Nunca é demais aprender! Se você quer se aprofundar um pouco mais sugiro as leituras abaixo:
- Documentação da Apple sobre conttentHuggingPriotity
- Documentação da Apple sobre conttentCompressionResistancePriotity
- Contém uma ilustração que ajuda muito
- Conceito de priorities
Espero ter contribuído para seus estudos! 😘