Preenchendo uma UILabel
solução completa. Atualizado para 2020.
Acontece que há três coisas que devem ser feitas.
1. Deve chamar textRect # forBounds com o novo tamanho menor
2. Deve substituir drawText pelo novo tamanho menor
3. Se uma célula de tamanho dinâmico, deve ajustar o intrinsicContentSize
No exemplo típico abaixo, a unidade de texto está em uma exibição de tabela, exibição de pilha ou construção semelhante, o que fornece uma largura fixa . No exemplo, queremos preenchimento de 60,20,20,24.
Assim, pegamos o intrinsicContentSize "existente" e adicionamos 80 à altura .
Repetir ...
Você precisa literalmente "obter" a altura calculada "até o momento" pelo mecanismo e alterar esse valor.
Acho esse processo confuso, mas é assim que funciona. Para mim, a Apple deve expor uma chamada chamada algo como "cálculo preliminar da altura".
Em segundo lugar, precisamos usar a chamada textRect # forBounds com nosso novo tamanho menor .
Assim, em textRect # forBounds, primeiro reduzimos o tamanho e depois chamamos super.
Alerta! Você deve ligar super depois , não antes!
Se você investigar cuidadosamente todas as tentativas e discussões nesta página, esse é o problema exato. Observe que algumas soluções "parecem quase funcionar", mas alguém relatará que, em determinadas situações, não funcionará. Esta é realmente a razão exata - confusamente você deve "ligar super depois", não antes.
Portanto, se você chama super-"na ordem errada", geralmente funciona, mas não para determinados comprimentos de texto específicos .
Aqui está um exemplo visual exato de "incorretamente fazendo super primeiro":
Observe que as margens 60,20,20,24 estão corretas, MAS o cálculo do tamanho está realmente errado, porque foi feito com o padrão "super first" no textRect # forBounds.
Fixo:
Observe que somente agora o mecanismo textRect # forBounds sabe como fazer o cálculo corretamente :
Finalmente!
Novamente, neste exemplo, o UILabel está sendo usado na situação típica em que a largura é fixa. Assim, em intrinsicContentSize, precisamos "adicionar" a altura extra geral que queremos. (Você não precisa "adicionar" de forma alguma à largura, isso não faria sentido, pois é fixo.)
Então, no textRect # forBounds, você obtém os limites "sugeridos até agora" pelo autolayout, subtrai suas margens e só então chama novamente para o mecanismo textRect # forBounds, ou seja, em super, o que fornecerá um resultado.
Por fim e simplesmente no drawText, é claro que você desenha na mesma caixa menor.
Ufa!
let UIEI = UIEdgeInsets(top: 60, left: 20, bottom: 20, right: 24) // as desired
override var intrinsicContentSize:CGSize {
numberOfLines = 0 // don't forget!
var s = super.intrinsicContentSize
s.height = s.height + UIEI.top + UIEI.bottom
s.width = s.width + UIEI.left + UIEI.right
return s
}
override func drawText(in rect:CGRect) {
let r = rect.inset(by: UIEI)
super.drawText(in: r)
}
override func textRect(forBounds bounds:CGRect,
limitedToNumberOfLines n:Int) -> CGRect {
let b = bounds
let tr = b.inset(by: UIEI)
let ctr = super.textRect(forBounds: tr, limitedToNumberOfLines: 0)
// that line of code MUST be LAST in this function, NOT first
return ctr
}
De novo. Observe que as respostas neste e em outro controle de qualidade "quase" correto sofrem com o problema na primeira imagem acima - o "super está no lugar errado" . Você deve forçar o tamanho maior em intrinsicContentSize e, em seguida, em textRect # forBounds, deve reduzir primeiro os limites da primeira sugestão e, em seguida, chame super.
Resumo: você deve "chamar super last " em textRect # forBounds
Esse é o segredo.
Observe que você não precisa e não deve chamar adicionalmente invalidar, sizeThatFits, needsLayout ou qualquer outra chamada forçada. Uma solução correta deve funcionar corretamente no ciclo normal de extração do autolayout.
Dica:
Se você estiver trabalhando com fontes monoespaçadas, aqui está uma ótima dica: https://stackoverflow.com/a/59813420/294884