A contentView.translatesAutoResizingMaskToConstraints de uma subclasse UICollectionViewCell deve ser definida como `false`?

TL; DR

Ao tentar dimensionar o UICollectionViewCells através do layout automático, você pode facilmente receber avisos de layout automático, mesmo com um exemplo simples.

Devemos estar definindocontentView.translatesAutoResizingMaskToConstraints = false se livrar deles?

Estou tentando criar um UICollectionView comcélulas de layout automático de dimensionamento automático.

Em viewDidLoad:

let layout = UICollectionViewFlowLayout()
layout.estimatedItemSize = CGSize(width: 10, height: 10)
collectionView.collectionViewLayout = layout

Meu celular é muito básico. É uma única vista azul com largura e altura 75 para fins de teste. As restrições são criadas fixando a vista na superview em todas as 4 arestas e fornecendo uma altura e largura.

class MyCell: UICollectionViewCell {
  override init(frame: CGRect) {
    view = UIView()

    super.init(frame: frame)

    view.backgroundColor = UIColor.blueColor()
    contentView.addSubview(view)

    installConstraints()
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  var view: UIView

  func installConstraints() {
    view.translatesAutoresizingMaskIntoConstraints = false

    var c: NSLayoutConstraint

    // pin all edges
    c = NSLayoutConstraint(item: contentView, attribute: .Leading, relatedBy: .Equal, toItem: view, attribute: .Leading, multiplier: 1, constant: 0)
    c.active = true
    c = NSLayoutConstraint(item: contentView, attribute: .Trailing, relatedBy: .Equal, toItem: view, attribute: .Trailing, multiplier: 1, constant: 0)
    c.active = true
    c = NSLayoutConstraint(item: contentView, attribute: .Top, relatedBy: .Equal, toItem: view, attribute: .Top, multiplier: 1, constant: 0)
    c.active = true
    c = NSLayoutConstraint(item: contentView, attribute: .Bottom, relatedBy: .Equal, toItem: view, attribute: .Bottom, multiplier: 1, constant: 0)
    c.active = true

    // set width and height
    c = NSLayoutConstraint(item: view, attribute: .Width, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 75)
    c.active = true
    c = NSLayoutConstraint(item: view, attribute: .Height, relatedBy: .Equal, toItem: nil, attribute: .NotAnAttribute, multiplier: 1, constant: 75)
    c.active = true
  }
}

Ao executar o código, recebo dois erros por não conseguir satisfazer simultaneamente as restrições:

Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. 
    Try this: 
        (1) look at each constraint and try to figure out which you don't expect; 
        (2) find the code that added the unwanted constraint or constraints and fix it. 
    (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSAutoresizingMaskLayoutConstraint:0x7fed88c1bac0 h=--& v=--& H:[UIView:0x7fed8a90c2f0(10)]>",
    "<NSLayoutConstraint:0x7fed8ab770b0 H:[UIView:0x7fed8a90bbe0(75)]>",
    "<NSLayoutConstraint:0x7fed8a90d610 UIView:0x7fed8a90c2f0.leading == UIView:0x7fed8a90bbe0.leading>",
    "<NSLayoutConstraint:0x7fed8ab005f0 H:[UIView:0x7fed8a90bbe0]-(0)-|   (Names: '|':UIView:0x7fed8a90c2f0 )>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7fed8ab770b0 H:[UIView:0x7fed8a90bbe0(75)]>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

(O segundo erro sobre as restrições verticais é omitido, mas é quase idêntico.)

Esses erros fazem sentido. Essencialmente, o contentView (0x7fed8a90c2f0) temtranslatesAutoresizingMask = true, assim, seus limites definidos para 10x10 (do tamanho estimado) criam esseNSAutoresizingMaskLayoutConstraint restrição. Não há como a visualização ter 10 pixels de largura e 75 pixels de largura, portanto gera um erro.

Tentei várias coisas diferentes para resolver esse problema, com apenas 2 delas parecendo funcionar.

Solução 1: defina pelo menos uma das restrições para 999 ou menos

Nesse caso, se eu alterar a restrição que fixa a parte inferior da exibição azul à parte inferior da prioridade contentView para 999 (e fizer o mesmo com a restrição à direita), isso resolverá o problema. Da mesma forma, eu poderia ter escolhido Top + Leading, ou Width + Height, desde que não tenha 1000 prioridades em todas as direções. Pelo menos um tem que "dar" para o layout inicial.

Embora possa parecer que isso causaria uma altura / largura incorreta, a exibição parece ter o tamanho correto.

Solução 2: definircontentView.translatesAutoresizingMaskIntoConstraints = false

Ao definir isso comofalse, ele essencialmente se livra doNSAutoresizingMaskLayoutConstraint restrição e não temos mais conflito. Por outro lado, como o contentView não é configurado com layout automático, é como se não mudasse o quadro, sua superview (a célula) nunca atualizaria seu tamanho, porque não há nada "pressionando" de volta contra isso.

No entanto, esta solução funciona de alguma forma mágica. Suponho que, em algum momento, a célula observe o quadro do contentView e decida "ei, você me define para esse tamanho, provavelmente significa que você deseja que a célula também seja desse tamanho" e cuida de redimensionar a célula para você.

Qual solução devemos usar?

Existem melhores soluções para esse problema? Qual das alternativas acima deve ser usada? Há alguma desvantagem nas duas soluções mencionadas acima, ou elas são essencialmente as mesmas e não importa qual delas você usa?

Notas:

A razão pela qual é mais difícil de ver em exemplos comoDGSelfSizingCollectionViewCells é porque eles contam com intrinsicContentSize + contentCompressionResistancePriority. Se você se livrar das minhas restrições de largura e altura, substitua-as por 1000 chamadas setContentCompressionResistancePriority (horizontal + vertical) e use algo com tamanho intrínseco (por exemplo, uma etiqueta com texto), você não verá mais os erros de layout automático. Isso implica quewidth@1000 comporta-sediferentemente que um intrinsicWidth +contentCompressionResistance@1000. Talvez o momento em que uma exibição obtenha sua largura intrínseca seja posterior à modificação do quadro da exibição de conteúdo.

Coisas que não funcionaram:

Percebi que, quando crio células por meio de um storyboard, geralmente nunca encontro esse problema. Quando examinei as diferenças noview de uma célula criada por meio de um storyboard e uma criada programaticamente, notei que oautoresizingMask foi RM + BM (36) no storyboard, mas 0 quando criado via código. Tentei configurar manualmente oautoresizingMask da minha visão azul para 36, mas isso não funcionou. A verdadeira razão pela qual a solução de storyboard funciona é que geralmente as restrições que você cria já estão perfeitamente definidas no storyboard (caso contrário, você teria erros de storyboard que precisam ser corrigidos). Como parte da correção desses erros, você pode alterar os limites da célula. Desde que chamainit?(coder:), os limites da célula já estão configurados corretamente. Então, mesmo que ocontentView.autoresizingMask = true, não há conflitos, pois ele já está definido no tamanho correto.No guia vinculado, ele vincula aesta postagem ao falar sobre a criação de restrições. Menciona que restrições devem ser criadas emupdateConstraints. No entanto, parece que essas foram as recomendações originais da Apple e que elas sãonão recomendo mais esta convenção para a maioria de suas restrições. No entanto, desde que esse artigo diz para criá-los emupdateConstraints Eu tentei sem sucesso. Eu obtive os mesmos resultados. Isso ocorre porque o quadro não foi atualizado no momento em que updateConstraints foi chamado (ainda era 10 x 10).Percebi problemas semelhantes quando você escolhe um tamanho estimado muito pequeno. Corrigi-o no passado, aumentando o tamanho estimado. Nesse caso, tentei 100x100, mas ainda causava os mesmos erros. Isso faz sentido ... uma visualização não pode ter 100 e 75 de largura ao mesmo tempo. (Observe que considero configurá-lo para 75 x 75 como uma solução não. Veja abaixo para mais informações.)

Não soluções:

Definindo o tamanho estimado para 75x75. Embora isso resolva os erros deste exemplo simples, não solucionará o problema para células que realmente são dimensionadas dinamicamente. Quero uma solução que funcione em qualquer lugar.Retornando as dimensões exatas da célula emcollectionView(_:layout:sizeForItemAtIndexPath:) (por exemplo, criando uma célula de dimensionamento). Eu quero uma solução que funcione diretamente com meu celular, sem precisar de escrituração ou cálculo extra.

questionAnswers(1)

yourAnswerToTheQuestion