Problema de restrições de layout automático no iOS7 em UITableViewCell

104

Estou usando restrições de layout automático programaticamente para fazer o layout de minhas células UITableView personalizadas e estou definindo corretamente os tamanhos das células em tableView:heightForRowAtIndexPath:

Ele está trabalhando muito bem no iOS6 e ele faz olhar bem em iOS7 bem

MAS quando executo o aplicativo no iOS7, eis o tipo de mensagem que vejo no console:

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2013-10-02 09:56:44.847 Vente-Exclusive[76306:a0b] 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) 
(
        "<NSLayoutConstraint:0xac4c5f0 V:|-(15)-[UIImageView:0xac47f50]   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
        "<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>",
        "<NSLayoutConstraint:0xac43680 V:[UIView:0xac4d0f0(1)]>",
        "<NSLayoutConstraint:0xac436b0 V:[UIView:0xac4d0f0]-(0)-|   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>

E, de fato, há uma das restrições nessa lista que não quero:

"<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"

e não consigo definir a translatesAutoresizingMaskIntoConstraintspropriedade de contentViewcomo NÃO =>, isso bagunçaria toda a célula.

44 é a altura de célula padrão, mas eu defini minhas alturas customizadas no delegado de visualização de tabela, então por que a célula contentView tem essa restrição? O que pode causar isso?

No iOS6 isso não está acontecendo e tudo parece bem no iOS6 e no iOS7.

Meu código é muito grande, então não vou postá-lo aqui, mas fique à vontade para pedir um pastebin se precisar.

Para especificar como estou fazendo isso, na inicialização da célula:

  • Eu crio todos os meus rótulos, botões, etc.
  • Eu defini sua translatesAutoresizingMaskIntoConstraintspropriedade como NÃO
  • Eu os adiciono como subvisões da contentViewcélula
  • Eu adiciono as restrições no contentView

Também estou profundamente interessado em entender por que isso acontece apenas no iOS7.

Alexis
fonte
Qual é a sua altura de célula personalizada definida?
Mike Pollard,
minha altura de célula personalizada está definida como 90
Alexis
Um colega teve o mesmo problema ontem, mas com a largura padrão de 320 (para um aplicativo de iPad).
jrturton,
Há um projeto de amostra aqui demonstrando esse mesmo problema: github.com/Alex311/TableCellWithAutoLayout Aqui estão algumas de minhas observações sobre ele: github.com/Alex311/TableCellWithAutoLayout/commit/…
smileyborg
Encontrei o mesmo erro, mas foi porque nunca inicializei a célula protótipo extra que estava usando para encontrar a altura da minha célula personalizada.
Steve Moser

Respostas:

132

Eu tive esse problema também .. Parece que o frame do contentView não é atualizado até que layoutSubviewsseja chamado, no entanto, o frame da célula é atualizado antes, deixando o frame do contentView definido como {0, 0, 320, 44}no momento em que as restrições são avaliadas.

Depois de examinar o contentView com mais detalhes, parece que o autoresizingMasks não estão mais sendo definidos.

Definir o autoresizingMask antes de restringir suas visualizações pode resolver este problema:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    if (self)
    {
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
        [self loadViews];
        [self constrainViews];
    }
    return self;
}
liamnichols
fonte
Isso faz o trabalho, obrigado cara. O que pode acontecer ao usar estilos de edição?
Alexis,
11
@ L14M333 Veja o código que postei neste comentário aqui - basicamente, você deve apenas ser capaz de definir self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);antes de adicionar suas restrições, o que deve resolver o problema.
smileyborg de
2
o autoresizingMask não funciona para mim: a restrição inicial desapareceu, mas os 3 novos foram adicionados "<NSAutoresizingMaskLayoutConstraint: 0x8b79740 h = - & - v = - & - UITableViewCellContentView: 0x8b44010.height == UITableViewCellScrollView: 0x8b4a130". "<NSAutoresizingMaskLayoutConstraint: 0x8b7b9f0 h = - & - v = - & - UITableViewCellScrollView: 0x8b4a130.height == LibraryCell: 0x8ac19d0.height>", "<NSAutoresizingMaskLayoutConstraint = 0x8b7cell - hr. 0x8ac19d0 (0)]> "
catanfetamina
1
Funciona para mim também, estou lutando há uma hora nisso! Obrigado! <3
mokagio
2
Depois de me livrar de outros problemas de layout automático (iOS 7 / iOS 8), descobri que usar self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);funciona tão bem quanto usar self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;. Eu coloquei isso updateConstraintsantes de adicionar as restrições à minha visualização de conteúdo.
teste de
35

Aparentemente, há algo errado com UITableViewCell e UICollectionViewCell no iOS 7 usando iOS 8 SDK.

Você pode atualizar o contentView da célula quando a célula é reutilizada desta forma:

Para UITableViewController estático:

#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }

    //your code goes here

    return cell;
}

#endif
#endif

Como os controladores de visualização de tabela estática são frágeis e podem ser facilmente quebrados se você implementar alguma fonte de dados ou métodos deletegate - há verificações que garantirão que este código só será compilado e executado no iOS 7

É semelhante para UITableViewController dinâmico padrão:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"CellID";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }
    //your code goes here       
    return cell;
}

Para este caso, não precisamos da verificação extra de compilação, já que a implementação deste método é necessária.

A ideia é a mesma para ambos os casos e para UICollectionViewCell, como comentado neste tópico: O problema de dimensionamento automático do quadro de UICollectionViewCell contentView na célula de protótipo do Storyboard (Xcode 6, iOS 8 SDK) ocorre quando executado no iOS 7 apenas

KoCMoHaBTa
fonte
1
Esta é a melhor resposta, ou pelo menos a melhor solução para mim. My Cell (regras de Autolayout) funcionou muito bem no iOS8 com XCode6 e funcionou muito bem com iOS7 e 6 com XCode 5. Eu atualizo o XCode para XCode6 e não está mais funcionando no iOS6 e 7. Então, obrigado !!!!
xarly
Isso não é mais um problema com o Xcode 6.1. Além disso, não use NSFoundationVersionNumber, consulte nshipster.com/swift-system-version-checking
onmyway133
1
@ onmyway133: Por que isso não é mais um problema no Xcode 6.1? Ainda tenho o problema, mesmo usando o Xcode 6.1 e iOS 8.1 SDK. O problema ocorre apenas no iOS 7. Alterar os limites ou definir uma máscara de redimensionamento automático resolve o problema. Mas usei a abordagem declarada na resposta mais votada ou nos comentários dela.
teste de
Sim, esta é a melhor resposta com base na minha pesquisa de 3 horas no Google. Outras "soluções" semelhantes não fornecem a lista completa de cell.contentView.autoresizingMask. Apenas este funciona para o meu projeto iPad 7.1 criado no Xcode 6.
Golden Thumb
13

Como as células estão sendo reutilizadas e a altura pode mudar com base no conteúdo, acho que, em geral, seria melhor definir a prioridade dos espaçamentos para menos do que o necessário.

No seu caso, o UIImageView tem um espaçamento de 15 na parte superior e a vista inferior tem um espaçamento de 0 na parte inferior. Se você definir a prioridade dessas restrições para 999 (em vez de 1000), o aplicativo não travará, porque as restrições não são necessárias.

Assim que o método layoutSubviews for chamado, a célula terá a altura correta e as restrições podem ser satisfeitas normalmente.

Steven
fonte
Você é um gênio! solução agradável e limpa, obrigado :)
Blacky
9

Ainda não encontrei uma boa solução para storyboards ... Algumas informações também aqui: https://github.com/Alex311/TableCellWithAutoLayout/commit/bde387b27e33605eeac3465475d2f2ff9775f163#commitcomment-4633188

Pelo que eles aconselham lá:

self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);

Eu induzi minha solução:

  • clique com o botão direito no storyboard
  • Abrir como -> Código-fonte
  • procure a string "44" lá
  • vai ser como

.

<tableView hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="44" ...
    <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatMessageCell" id="bCG-aU-ivE" customClass="ChatMessageCell">
        <rect key="frame" x="0.0" y="22" width="320" height="44"/>

.

  • substitua rowHeight = "44" por rowHeight = "9999" e altura = "44" por altura = "9999"
  • clique com o botão direito no storyboard
  • Abrir como -> Interface Builder
  • execute seu aplicativo e verifique o resultado
catanfetamina
fonte
Isso funcionou para mim. Eu criei um UITableViewController no storyboard onde algumas células foram criadas a partir do protótipo e outras inteiramente no código, instanciando uma classe.
Atharva
3

Basta aumentar a altura padrão da célula. Parece que o problema é que o conteúdo da célula é maior do que o tamanho de célula padrão (inicial), violando algumas restrições de não negatividade até que a célula seja redimensionada para seu tamanho real.

Vadim Yelagin
fonte
Na verdade, minhas células são mais altas do que o tamanho padrão, mas como acontece que o tamanho padrão seja usado, uma vez que o delegado do tableview implementa tableView: heightForRowAtIndexPath:?
Alexis
Provavelmente as células são criadas com o tamanho padrão e então redimensionadas para o tamanho real.
Vadim Yelagin,
na verdade, ele faz o trabalho, mas por que não acontece no iOS6 então?
Alexis,
3
@jafar iOS 7 mudou muitas coisas em torno das células de visualização de tabela. No iOS 7, agora há uma visualização de rolagem (do tipo UITableViewCellScrollView) entre a célula de visualização de tabela e a visualização de conteúdo; isso provavelmente explica a diferença entre iOS 6 e 7 aqui.
smileyborg
3

Talvez defina a prioridade da visualização maior que 750 e menos de 1000 pode resolver.

"<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
heramerom
fonte
2

Eu tive o mesmo problema. Minha solução baseada em outras acima:

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        // initialize my stuff
        [self layoutSubviews]; // avoid debugger warnings regarding constraint conflicts
    }
    return self;
}

Atenciosamente Alessandro

Cormentis
fonte
2
Dos documentos da Apple:You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
Ricardo Sanchez-Saez
Eu quero começar uma religião orando por você. Estou pesquisando há HORAS tentando corrigir esse problema e isso o resolve (além de definir contentView.bounds como CGRectMake (0, 0, 99999, 99999);) - ESTE É UM BUG NO CÓDIGO DE APPLES. Então, eu respeitosamente discordo dos docs de maçãs dizendo para não chamar essa função. Apple, conserte sua merda.
Ryan Copley,
0

Também enfrentei esse problema e nenhuma das sugestões ajudou. No meu caso, eu tinha uma célula de seleção de tamanho, e ela continha collectionView dentro (cada célula collectionView continha uma imagem em tamanho real). Agora, a altura do tamanho da célula era um pouco maior (60) do que a collectionView (50) e as imagens dentro dela (50). Por causa disso, a visão collectionView tem a restrição de alinhamento inferior à supervisualização com um valor de 10. Nesse caso, um aviso, e a única maneira de corrigir isso era tornar a altura da célula igual à de sua collectionView.

Centurião
fonte
0

Acabei não usando UITableViewCell no designer ... Eu crio lá uma visualização personalizada e adiciono-a à visualização de conteúdo da célula programaticamente ... Com algumas categorias de ajuda também não há código clichê ...

Renetik
fonte
0

Se funcionar bem no iOS8 e receber o aviso no iOS7, você pode pesquisar o código-fonte do storyboard, encontrar o tableviewCell correto e anexar o atributo rect após a linha tableviewCell. Graças a kuchumovn .

<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" .../>
                                <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
NSDeveloper
fonte