O problema de redimensionamento automático do quadro do UICollectionViewCell contentView na célula do protótipo Storyboard (Xcode 6, iOS 8 SDK) ocorre quando executado apenas no iOS 7

158

Estou usando o Xcode 6 Beta 3, iOS 8 SDK. Crie o iOS 7.0 de destino usando o Swift. Consulte o meu problema passo a passo com as capturas de tela abaixo.

Eu tenho um UICollectionView no Storyboard. 1 protótipo UICollectionViewCell que contém 1 rótulo no centro (sem regra de redimensionamento automático). O fundo roxo era para marcar um contentView que é gerado em tempo de execução pela célula, eu acho. Essa visualização será redimensionada adequadamente com base no meu UICollectionViewLayoutDelegate eventualmente, mas não no iOS 7. Observe que estou usando o Xcode 6 e o ​​problema só acontece no iOS 7.

Quando crio o aplicativo no iOS 8. Tudo está bem.

Nota: Roxo é o contentView , Azul é meu UIButton com canto arredondado.

http://i.stack.imgur.com/uDNDY.png

No entanto, no iOS 7, todos os subViews dentro da célula diminuem repentinamente para o quadro de (0,0,50,50) e nunca mais estão em conformidade com minha regra de redimensionamento automático.

http://i.stack.imgur.com/lOZH9.png

Presumo que este seja um erro no iOS 8 SDK ou Swift ou talvez no Xcode?


Atualização 1: Esse problema ainda existe no Xcode 6.0.1 oficial! A melhor solução é como o que o KoCMoHaBTa sugeriu abaixo, definindo o quadro em cellForItem da célula (você precisa subclassificar sua célula). Descobriu-se que esta é uma incompatibilidade entre o iOS 8 SDK e o iOS 7 (verifique a resposta da ecotax abaixo citada pela Apple).

Atualização 2: cole este código no início do seu cellForItem e tudo ficará bem:

/** Xcode 6 on iOS 7 hot fix **/
cell.contentView.frame = cell.bounds;
cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
/** End of Xcode 6 on iOS 7 hot fix **/
thkeen
fonte
1
Descobri que esse problema ainda existe no Xcode 6 Beta 5. Alguém experimentou isso também?
thkeen
2
Estou lutando com isso agora no meu projeto. O iOS 7 e o iOS 8 criados com o Xcode 5 estão bem. O iOS 8 construído com o Xcode 6 beta 6 parece bom. O iOS 7 criado com o Xcode 6 beta 6 está com o problema que você está descrevendo. Usando Reveal, posso ver que meu UICollectionViewCell foi dimensionado corretamente. Mas o contentView da célula não foi redimensionado, mesmo sendo pai, o UICollectionViewCell tem as subvisões de redimensionamento automático ativadas. O tamanho do contentView é definido como o storyboard. Não estou usando o autolayout neste projeto. Meu projeto é completamente objetivo-c.
Del Brown
2
Eu só queria acrescentar que esse problema ainda existe a partir da semente Xcode 6 / iOS 8 GM. A resposta de @ DanielPlamann para forçar o contentViewredimensionamento da célula funciona bem para solucionar o problema. Eu acho que no iOS 8 a Apple mudou algo sobre a maneira como as visualizações de conteúdo da célula são tratadas quando criadas no Interface Builder (que ainda é uma caixa preta). Mas o fato de alterar o comportamento ao segmentar o iOS 7 é certamente um bug.
Stuart
O mesmo aqui com o Xcode 6 GM, layout automático e uma célula baseada em ponta. Eu conserto fixando as contentViewbordas nas bordas da célula.
Sergiou87 10/09/14
3
Eu baixei o xcode 6.1, mas ainda vejo o mesmo problema no simulador.
Haitao Li

Respostas:

169

contentView está quebrado. Também pode ser corrigido no awakeFromNib

ObjC:

- (void)awakeFromNib {

    [super awakeFromNib];

    self.contentView.frame = self.bounds;
    self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}

Swift3:

override func awakeFromNib() {
    super.awakeFromNib()

    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
}
Igor Palaguta
fonte
3
Fez o truque sem self.contentView.frame = self.bounds; Acha que preciso disso? Obrigado mesmo assim!
Michal Shatz
Olá Michal, concordo com você. Mas, para ter 100% de certeza, como é melhor funcionar nos próximos iOS para adicioná-lo, eu acho.
Igor Palaguta 21/09
5
Eu gosto dessa resposta, mas você precisa chamar [super awakeFromNib]; Além disso, estou pensando em adicionar uma verificação para iOS 7.1 e menos, porque não tenho certeza de como adicionar essas máscaras de redimensionamento afeta o comportamento padrão no iOS8.
GingerBreadMane 23/09
Se você não estiver usando nibs ou storyboards, isso também funcionará se você o aplicar em applyLayoutAttributes:
cetcet
Isso não funciona para mim. Não estou usando o Autolayout !! Alguma ajuda?
thatzprem
61

Encontrei o mesmo problema e pedi ajuda ao Apple DTS. A resposta deles foi:

No iOS 7, as visualizações de conteúdo das células se dimensionavam por meio de máscaras de redimensionamento automático. No iOS 8, isso foi alterado, as células pararam de usar as máscaras de redimensionamento automático e começaram a dimensionar a exibição do conteúdo em layoutSubviews. Se uma ponta é codificada no iOS 8 e, em seguida, decodificada no iOS 7, você terá uma exibição de conteúdo sem uma máscara de redimensionamento automático e nenhum outro meio pelo qual se dimensionar. Portanto, se você alterar o quadro da célula, a exibição do conteúdo não será exibida.

Os aplicativos que estão sendo implantados no iOS 7 terão que solucionar esse problema dimensionando a própria exibição de conteúdo, adicionando máscaras de redimensionamento automático ou restrições.

Acho que isso significa que não é um bug no XCode 6, mas uma incompatibilidade entre o iOS 8 SDK e o iOS 7 SDK, que afetará você se você atualizar para o Xcode 6, porque ele começará automaticamente a usar o iOS 8 SDK.

Como comentei antes, a solução alternativa que Daniel Plamann descreveu funciona para mim. Porém, os descritos por Igor Palaguta e KoCMoHaBTa parecem mais simples e parecem fazer sentido dar a resposta do Apple DTS, então tentarei isso mais tarde.

ecotax
fonte
Isso é interessante, mas ainda espero que eles consertem. Esse comportamento foi introduzido apenas no Xcode 6 GM, que adicionou suporte ao iPhone 6. Estava funcionando bem nos betas anteriores. Eu até trouxe um projeto de volta para uma versão beta anterior depois que percebi e funcionou como esperado. Espero que todos arquivem bugs com a Apple nesta questão.
Art21 de
@arton Arquivei um relatório de bug no mesmo dia em que pedi ajuda à DTS. Foi fechado como um duplicado da 18312246. Não tenho certeza de quanto isso ajuda.
Ecotax #
Eu usei a solução alternativa dimensionando o ContentView e funciona para mim. - (CGSize) collectionViewContentSize {return CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); }
Jesús Hurtado
60

Encontrei o mesmo problema e espero que a Apple conserte isso com a próxima versão do Xcode. Enquanto isso, uso uma solução alternativa. Na minha UICollectionViewCellsubclasse, acabei de substituir layoutSubviewse redimensionar o contentView manualmente, caso o tamanho seja diferente do collectionViewCelltamanho.

- (void)layoutSubviews
{
  [super layoutSubviews];

  BOOL contentViewIsAutoresized = CGSizeEqualToSize(self.frame.size, self.contentView.frame.size);

  if( !contentViewIsAutoresized) {
    CGRect contentViewFrame = self.contentView.frame;
    contentViewFrame.size = self.frame.size;
    self.contentView.frame = contentViewFrame;
  }
}
Daniel Plamann
fonte
Sim, eu usei um trabalho semelhante, mas o fiz em cellForItem / cellForRow, que também funciona.
thkeen
1
essa é a melhor solução. Adicionar isso em cellForItem / cellForRow causará artefatos estranhos se você estiver girando o dispositivo e o tamanho da célula mudar.
spybart 12/09
Oi! Eu tenho um problema com este código. Pela primeira vez, o conteúdo booleanoViewIsAutoresized será verdadeiro se for carregado a partir do storyboard ou célula do protótipo. Somente quando você fizer um reloadData, o segundo estará correto. Então você realmente não precisa verificar o tamanho. Em vez disso, basta fazer: self.contentView.frame = self.bounds;
thkeen
Confirmado: devemos fazê-lo em cellForItem ou cellForRow porque layoutSubviews é chamado somente após o retorno da célula. Qualquer coisa que você faça antes, como desenhar coisas, será calculada incorretamente.
thkeen 18/09/14
1
Talvez seja melhor colocar [cell layoutIfNeeded];cellForItem ou cellForRow?
wtorsi
38

Outra solução é definir o tamanho do contentView e as máscaras de redimensionamento automático -collectionView:cellForItemAtIndexPath:da seguinte forma:

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

     static NSString *cellID = @"CellID";

     UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellID forIndexPath:indexPath];

     // Set contentView's frame and autoresizingMask
     cell.contentView.frame = cell.bounds;
     cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;

     // Your custom code goes here

     return cell;
}

Isso também funciona com o Layout automático, pois as máscaras de redimensionamento automático são convertidas em restrições.

KoCMoHaBTa
fonte
2
Essa é uma solução superior, pois funciona com qualquer tipo de célula sem subclassificação e não requer alterações em vários locais quando você está usando mais de um tipo de célula na visualização da coleção.
Nacross 14/10
6

No Xcode 6.0.1, o contentView para UICollectionViewCell está quebrado para dispositivos iOS7. Também pode ser corrigido adicionando restrições apropriadas ao UICollectionViewCell e seu contentView nos métodos awakeFromNib ou init.

        UIView *cellContentView = self.contentView;
        cellContentView.translatesAutoresizingMaskIntoConstraints = NO;

        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
        [self addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[cellContentView]|"
                                                                     options:0
                                                                     metrics:0
                                                                       views:NSDictionaryOfVariableBindings(cellContentView)]];
SerJ_G
fonte
Só isso funciona agora, você precisa fazer isso apenas para o IOS 8 e eu tenho que chamá-lo após cada desenfileiramento! Mas isso também não é a solução ideal porque a seleção múltipla não funciona como deveria visualmente ...
Renetik
Melhor é usar autolayout realmente, com este bug você salvar ironicamente vez por evento de uso autolayout em caso simples ..
Renetik
A máscara não funciona para mim; no entanto, definir as restrições funciona!
entropid 18/03/2015
4

Isso não funcionará corretamente sem nenhuma das outras soluções mencionadas devido a um erro no Xcode 6 GM com a forma como o Xcode compila arquivos xib no formato da ponta. Embora eu não possa dizer com 100% de certeza que está relacionado ao Xcode e não está relacionado ao tempo de execução, estou muito confiante - eis como posso mostrá-lo:

  1. Build + Execute o aplicativo no Xcode 5.1.
  2. Vá para o diretório do aplicativo simulador e copie o arquivo .nib compilado para o xib com o qual você está tendo problemas.
  3. Build + Execute o aplicativo no Xcode 6 GM.
  4. Pare a aplicação.
  5. Substitua o arquivo .nib na pasta simulador do aplicativo recém-criado pelo arquivo .nib criado usando o Xcode 5.1
  6. Reinicie o aplicativo a partir do simulador, NÃO do Xcode.
  7. Seu celular carregado a partir desse .nib deve funcionar conforme o esperado.

Espero que todos que leem esta pergunta registrem um radar na Apple. Este é um problema enorme e precisa ser resolvido antes da versão final do Xcode.

Edit: À luz do post de ecotax , eu só queria atualizar isso para dizer que agora são confirmadas diferenças de comportamento entre a criação no iOS 8 e o iOS 7, mas não é um bug. Meu hack corrigiu o problema porque a criação no iOS 7 adicionou a máscara de redimensionamento automático à exibição de conteúdo necessária para fazer esse trabalho, que a Apple não adiciona mais.

Acey
fonte
Eu não tenho certeza se eles legalmente pode liberar um xCode diferente do que a versão GM
Mabedan
Haha eles vão para a cadeia? = P Mas com toda a seriedade, com certeza eles podem. Eles tinham várias versões GM para o Mavericks, se você não se lembra. Não é muito comum, mas pode acontecer.
Acey
4

As respostas deste post funcionam, o que eu nunca entendi é por que funciona.

Primeiro, existem duas "regras":

  1. Para visualizações criadas programaticamente (por exemplo [UIView new]), a propriedade translatesAutoresizingMaskIntoConstraintsestá configurada paraYES
  2. As visualizações criadas no construtor de interface, com o AutoLayout ativado, terão a propriedade translatesAutoresizingMaskIntoConstraintsconfigurada comoNO

A segunda regra parece não se aplicar às visualizações de nível superior para as quais você não define restrições. (Ex. A visualização do conteúdo)

Ao olhar para uma célula do Storyboard, observe que a célula não está contentViewexposta. Não estamos "controlando" a contentViewApple.

Mergulhe fundo no código fonte do storyboard e veja como a contentViewcélula é definida:

<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">

Agora, as subvisões da célula (observe o translatesAutoresizingMaskIntoConstraints="NO"):

<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="NaT-qJ-npL" userLabel="myCustomLabel">

O contentViewnão está translatesAutoresizingMaskIntoConstraintsdefinido como NO. Além disso, falta definição de layout, talvez por causa do que a @ecotax disse .

Se olharmos para o contentView, ele tem uma máscara de redimensionamento automático, mas nenhuma definição para isso: <autoresizingMask key="autoresizingMask"/>

Portanto, existem duas conclusões:

  1. contentView translatesAutoresizingMaskIntoConstraintsestá definido para YES.
  2. contentView falta definição de um layout.

Isso nos leva a duas soluções que foram discutidas.

Você pode definir as máscaras de redimensionamento manual manualmente em awakeFromNib:

self.contentView.frame = cell.bounds;
self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Ou você pode definir o contentView translatesAutoresizingMaskIntoConstraintsque NOem awakeFromNibe definir restrições no - (void)updateConstraints.

kgaidis
fonte
4

Esta é a versão Swift da resposta de @ Igor, que é aceita e agradecemos a sua boa resposta, companheiro.

Primeiro, vá para sua UICollectionViewCellsubclasse e cole o seguinte código, pois está dentro da classe.

override func awakeFromNib() {
    super.awakeFromNib()
    self.contentView.frame = self.bounds
    self.contentView.autoresizingMask = [.FlexibleHeight, .FlexibleWidth]
}

A propósito, estou usando o Xcode 7.3.1 e o Swift 2.3. A solução foi testada no iOS 9.3, que está funcionando perfeitamente.

Obrigado, espero que isso tenha ajudado.

onCompletion
fonte
desculpe pelo meu mau Inglês, eu quis dizer "funciona como um encanto" :)
Aznix
@Aznix No issue .. :)
onCompletion 23/08
2

Em breve, coloque o seguinte código na subclasse da célula de exibição de coleção:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
Ian
fonte
Esta foi a resposta que eu estava procurando. Eu costumava substituir setBounds no Objective-C para corrigir esse problema (não sabia como escrevê-lo no Swift). Obrigado :)
Matthew Cawley
1

Descobri que também há problemas com o contentViewdimensionamento no iOS 8. Ele tende a ser definido muito tarde no ciclo, o que pode causar conflitos de restrição temporários. Para resolver isso, adicionei o seguinte método em uma categoria de UICollectionViewCell:

- (void)fixupContentView
{
#if __IPHONE_OS_VERSION_MAX_ALLOWED < 80100
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) {
        self.contentView.frame = self.bounds;
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    } else {
        [self layoutIfNeeded];
    }
#endif
#endif
}

Este método deve ser chamado após desenfileirar a célula.

phatmann
fonte
Seria ideal se esse método fosse chamado automaticamente. Eu não amo isso, mas isso pode ser feito por swizzling dequeueReusableCellWithReuseIdentifier:forIndexPath:.
Phatmann #
A Apple parece ter corrigido esse problema na versão 8.1 do SDK do iOS no Xcode 6.1 GM (Build 6A1042b). Portanto, atualizei o código acima para não executar ao usar o SDK 8.1. Depois que sua equipe mudar para o Xcode 6.1, você poderá remover completamente esse hack.
Phatmann # 7/14
1

Corrigi isso:

override func layoutSubviews() {
   contentView.superview?.frame = bounds
   super.layoutSubviews()
}

veja aqui

Serluca
fonte
-1

Apenas certifique-se de marcar a caixa de seleção "Autoresize subviews" na ponta dessa célula de exibição de coleção. Funcionará bem no iOS 8 e no iOS 7.

Deepak GM
fonte
Não, não faz. Esta é uma célula protótipo incorporada.
thkeen 21/01