Le problème de redimensionnement automatique du cadre de UICollectionViewCell contentView dans la cellule prototype de Storyboard (Xcode 6, iOS 8 SDK) se produit lors de l'exécution sur iOS 7 uniquement

158

J'utilise Xcode 6 Beta 3, iOS 8 SDK. Construisez Target iOS 7.0 à l'aide de Swift. Veuillez vous référer à mon problème étape par étape avec les captures d'écran ci-dessous.

J'ai un UICollectionView dans Storyboard. 1 prototype UICollectionViewCell qui contient 1 étiquette au centre (pas de règle de redimensionnement automatique). Le fond violet était de marquer un contentView qui est généré au moment de l'exécution par la cellule, je suppose. Cette vue sera redimensionnée correctement en fonction de mon UICollectionViewLayoutDelegate éventuellement, mais pas sur iOS 7. Notez que j'utilise Xcode 6 et que le problème ne se produit que sur iOS 7.

Lorsque je crée l'application sur iOS 8. Tout va bien.

Remarque: Le violet est le contentView , le bleu est mon UIButton avec un coin arrondi.

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

Cependant, sur iOS 7, toutes les sous-vues à l'intérieur de la cellule se réduisent soudainement à l'image de (0,0,50,50) et ne se conforme plus à ma règle de redimensionnement automatique.

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

Je suppose qu'il s'agit d'un bogue dans le SDK iOS 8 ou Swift ou peut-être Xcode?


Mise à jour 1: Ce problème existe toujours dans le Xcode 6.0.1 officiel! La meilleure solution est celle suggérée par KoCMoHaBTa ci-dessous en définissant le cadre dans cellForItem de la cellule (vous devez cependant sous-classer votre cellule). Il s'est avéré qu'il s'agissait d'une incompatibilité entre le SDK iOS 8 et iOS 7 (consultez la réponse d'écotaxe ci-dessous citée par Apple).

Mise à jour 2: collez ce code au début de votre cellForItem et les choses devraient bien se passer:

/** 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
la source
1
J'ai découvert que ce problème existe toujours dans Xcode 6 Beta 5. Quelqu'un l'a-t-il également rencontré?
thkeen
2
J'ai du mal avec ça en ce moment dans mon projet. iOS 7 et iOS 8 construits à l'aide de Xcode 5 semblent bien. iOS 8 construit avec Xcode 6 beta 6 a l'air bien. iOS 7 construit avec Xcode 6 beta 6 rencontre le problème que vous décrivez. En utilisant Reveal, je peux voir que mon UICollectionViewCell a été correctement dimensionné. Mais le contentView de la cellule n'a pas été redimensionné, même s'il est parent, UICollectionViewCell a activé la fonction de redimensionnement automatique des sous-vues. La taille de contentView est définie sur ce que le storyboard a. Je n'utilise pas la mise en page automatique dans ce projet. Mon projet est complètement objectif-c.
Del Brown
2
Je voulais juste ajouter que ce problème existe toujours à partir de la graine Xcode 6 / iOS 8 GM. La réponse de @ DanielPlamann pour forcer le contentViewredimensionnement avec la cellule fonctionne très bien pour contourner le problème. Je suppose que dans iOS 8, Apple a changé quelque chose dans la façon dont les vues du contenu des cellules sont gérées lors de leur création dans Interface Builder (qui est toujours un peu une boîte noire de toute façon). Mais le fait qu'il change de comportement en ciblant iOS 7 est sûrement un bogue.
Stuart
Idem ici avec Xcode 6 GM, disposition automatique et une cellule basée sur la pointe. Je le fixe en épinglant les contentViewbords sur les bords des cellules.
sergiou87
3
J'ai téléchargé xcode 6.1 mais je vois toujours le même problème dans le simulateur.
Haitao Li

Réponses:

169

contentView est cassé. Il peut également être corrigé dans 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
la source
3
A fait le tour sans self.contentView.frame = self.bounds; Tu penses que j'en ai besoin? Merci quand même!
Michal Shatz
Bonjour Michal, d'accord avec vous. Mais pour être sûr à 100%, comment cela fonctionnera-t-il mieux dans les prochains iOS pour l'ajouter, je pense.
Igor Palaguta
5
J'aime cette réponse mais vous devez appeler [super awakeFromNib]; De plus, j'envisage d'ajouter une vérification pour iOS 7.1 et moins, car je ne sais pas comment l'ajout de ces masques de redimensionnement affecte le comportement par défaut sur iOS8.
GingerBreadMane
Si vous n'utilisez pas de pointes ou de storyboards, cela fonctionne également si vous le mettez dans applyLayoutAttributes:
cetcet
Cela ne fonctionne pas pour moi. Je n'utilise pas Autolayout !! Une assistance?
thatzprem
61

J'ai rencontré le même problème et j'ai demandé de l'aide à Apple DTS. Leur réponse fut:

Dans iOS 7, les vues de contenu des cellules se dimensionnaient via des masques de redimensionnement automatique. Dans iOS 8, cela a été modifié, les cellules ont cessé d'utiliser les masques de redimensionnement automatique et ont commencé à dimensionner la vue du contenu dans layoutSubviews. Si une pointe est encodée dans iOS 8 puis décodée sur iOS 7, vous aurez une vue du contenu sans masque de redimensionnement automatique et aucun autre moyen de se dimensionner. Donc, si jamais vous changez le cadre de la cellule, la vue du contenu ne suivra pas.

Les applications en cours de déploiement vers iOS 7 devront contourner ce problème en dimensionnant la vue de contenu elle-même, en ajoutant des masques de redimensionnement automatique ou en ajoutant des contraintes.

Je suppose que cela signifie que ce n'est pas un bogue dans XCode 6, mais une incompatibilité entre le SDK iOS 8 et le SDK iOS 7, qui vous frappera si vous passez à Xcode 6, car il commencera automatiquement à utiliser le SDK iOS 8.

Comme je l'ai déjà dit, la solution de contournement décrite par Daniel Plamann fonctionne pour moi. Ceux décrits par Igor Palaguta et KoCMoHaBTa semblent plus simples cependant, et semblent avoir du sens en donnant la réponse d'Apple DTS, donc je les essayerai plus tard.

écotaxe
la source
C'est intéressant, mais j'espère toujours qu'ils le répareront. Ce comportement n'a été introduit que dans Xcode 6 GM qui a ajouté la prise en charge de l'iPhone 6. Cela fonctionnait bien dans les versions bêta précédentes. J'ai même ramené un projet à une version bêta antérieure après l'avoir remarqué et cela a fonctionné comme prévu. J'espère que tout le monde dépose des bogues avec Apple sur ce problème.
arton
@arton J'ai déposé un rapport de bogue le jour même où j'ai demandé de l'aide à DTS. Il a été fermé en tant que duplicata de 18312246. Je ne sais pas à quel point cela aide.
écotaxe
J'ai utilisé le travail autour en dimensionnant le ContentView et fonctionne pour moi. - (CGSize) collectionViewContentSize {retourne CGSizeMake (self.collectionView.bounds.size.width, self.collectionView.bounds.size.height); }
Jesús Hurtado
60

J'ai rencontré le même problème et j'espère qu'Apple le résoudra avec la prochaine version de Xcode. En attendant, j'utilise une solution de contournement. Dans ma UICollectionViewCellsous - classe, je viens de layoutSubviewsremplacer et de redimensionner manuellement le contentView au cas où la taille diffère de la collectionViewCelltaille.

- (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
la source
Oui, j'ai utilisé un travail similaire, mais je l'ai fait dans cellForItem / cellForRow qui fonctionne également.
thkeen le
1
C'est la meilleure solution. L'ajout de cela dans cellForItem / cellForRow provoquera des artefacts étranges si vous faites pivoter l'appareil et que la taille de la cellule change.
spybart
Salut! J'ai un problème avec ce code. Pour la première fois, la valeur booléenne contentViewIsAutoresized sera vraie si elle est chargée à partir du storyboard ou de la cellule prototype. Ce n'est que lorsque vous effectuez un reloadData que le second sera correct. Vous n'avez donc pas vraiment besoin de vérifier la taille. Au lieu de cela, faites simplement: self.contentView.frame = self.bounds;
thkeen le
Confirmé: nous devons le faire dans cellForItem ou cellForRow car layoutSubviews n'est appelé qu'après le retour de la cellule. Tout ce que vous faites avant cela, comme dessiner, sera mal calculé.
thkeen
1
Hm, il vaut peut-être mieux le placer [cell layoutIfNeeded];dans cellForItem ou cellForRow?
wtorsi
38

Une autre solution consiste à définir la taille de contentView et les masques de redimensionnement automatique -collectionView:cellForItemAtIndexPath:comme suit:

- (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;
}

Cela fonctionne également avec la mise en page automatique, car les masques de redimensionnement automatique sont convertis en contraintes.

KoCMoHaBTa
la source
2
Il s'agit d'une solution supérieure car elle fonctionne avec n'importe quel type de cellule sans sous-classification et ne nécessite pas de modifications à plusieurs endroits lorsque vous utilisez plusieurs types de cellule dans votre vue de collection.
nacross
6

Dans Xcode 6.0.1, contentView pour UICollectionViewCell est interrompu pour les appareils iOS7. Il peut également être corrigé en ajoutant des contraintes appropriées à UICollectionViewCell et à son contentView dans les méthodes 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
la source
Seulement cela fonctionne maintenant, vous devez le faire juste pour IOS 8, et je dois l'appeler après chaque retrait de la file d'attente! Mais ce n'est pas non plus la solution idéale car la sélection multiple ne fonctionne pas comme elle le devrait visuellement ...
Renetik
Le mieux est d'utiliser vraiment la mise en page automatique, avec ce bogue, vous gagnez du temps ironiquement en utilisant l'événement de mise en page automatique dans un cas simple ..
Renetik
Le masque ne fonctionne pas pour moi; cependant la définition des contraintes fonctionne!
entropid
4

Cela ne fonctionnera pas correctement sans aucune des autres solutions de contournement mentionnées en raison d'un bogue dans Xcode 6 GM avec la façon dont Xcode compile les fichiers xib au format nib. Bien que je ne puisse pas dire avec certitude à 100% qu'il est lié à Xcode et n'a pas à voir avec l'exécution, je suis très confiant - voici comment je peux le montrer:

  1. Construire + Exécutez l'application dans Xcode 5.1.
  2. Accédez au répertoire de l'application du simulateur et copiez le fichier .nib compilé pour le xib avec lequel vous rencontrez des problèmes.
  3. Build + Exécutez l'application dans Xcode 6 GM.
  4. Arrêtez l'application.
  5. Remplacez le fichier .nib dans le dossier simulateur de l'application nouvellement créée par le fichier .nib créé à l'aide de Xcode 5.1
  6. Relancez l'application depuis le simulateur, PAS depuis Xcode.
  7. Votre cellule chargée à partir de ce fichier .nib devrait fonctionner comme prévu.

J'espère que tous ceux qui liront cette question déposeront un radar auprès d'Apple. C'est un problème ÉNORME et doit être résolu avant la version finale de Xcode.

Edit: À la lumière du message d' écotaxe , je voulais juste mettre à jour cela pour dire qu'il est maintenant confirmé des différences de comportement entre la construction dans iOS 8 et iOS 7, mais pas un bogue. Mon hack a résolu le problème car la construction sur iOS 7 a ajouté le masque de redimensionnement automatique à la vue de contenu nécessaire pour que cela fonctionne, ce qu'Apple n'ajoute plus.

Acey
la source
Je ne sais pas s'ils peuvent légalement publier un xCode différent de la version GM
Mabedan
Haha iront-ils en prison? = P Mais sérieusement, bien sûr qu'ils le peuvent. Ils avaient plusieurs versions GM pour Mavericks si vous ne vous en souvenez pas. Ce n'est pas trop courant mais cela peut arriver.
Acey
4

Les réponses dans ce post fonctionnent, ce que je n'ai jamais compris, c'est pourquoi cela fonctionne.

Premièrement, il existe deux "règles":

  1. Pour les vues créées par programme (Ex. [UIView new]), La propriété translatesAutoresizingMaskIntoConstraintsest définie surYES
  2. Les vues créées dans le générateur d'interface, avec la mise en page automatique activée, auront la propriété translatesAutoresizingMaskIntoConstraintsdéfinie surNO

La deuxième règle ne semble pas s'appliquer aux vues de niveau supérieur pour lesquelles vous ne définissez pas de contraintes. (Ex. La vue du contenu)

Lorsque vous regardez une cellule Storyboard, notez que la cellule n'a pas son contentViewexposé. Nous ne «contrôlons» pas contentView, Apple l'est.

Plongez dans le code source du storyboard et voyez comment la contentViewcellule est définie:

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

Maintenant, les sous-vues de la cellule (notez le translatesAutoresizingMaskIntoConstraints="NO"):

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

Le contentViewne l' a pas est translatesAutoresizingMaskIntoConstraintsréglé sur NO. De plus, il manque de définition de mise en page, peut-être à cause de ce que @ecotax a dit .

Si nous regardons dans le contentView, il a un masque de redimensionnement automatique, mais aucune définition pour celui-ci: <autoresizingMask key="autoresizingMask"/>

Il y a donc deux conclusions:

  1. contentView translatesAutoresizingMaskIntoConstraintsest réglé sur YES.
  2. contentView manque de définition d'une mise en page.

Cela nous amène à deux solutions dont on a parlé.

Vous pouvez définir manuellement les masques de redimensionnement automatique dans awakeFromNib:

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

Vous pouvez également définir contentView translatesAutoresizingMaskIntoConstraintssur NOdans awakeFromNibet définir des contraintes dans - (void)updateConstraints.

kgaidis
la source
4

Ceci est la version Swift de la réponse de @ Igor qui est acceptée et merci pour votre gentil compagnon de réponse.

Accédez d' abord à votre UICollectionViewCellsous-classe et collez le code suivant tel qu'il se trouve dans la classe.

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

Au fait, j'utilise Xcode 7.3.1 et Swift 2.3. La solution est testée sur iOS 9.3 qui fonctionne parfaitement.

Merci, j'espère que cela a aidé.

en complément
la source
désolé pour mon mauvais anglais, je voulais dire "ça marche comme un charme" :)
Aznix
@Aznix No issue .. :)
onCompletion
2

Dans swift, placez le code suivant dans la sous-classe de cellule de vue de collection:

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
Ian
la source
C'était la réponse que je cherchais. J'avais l'habitude de remplacer setBounds dans Objective-C pour résoudre ce problème (je ne savais pas comment l'écrire dans Swift). Merci :)
Matthew Cawley
1

J'ai constaté qu'il y avait également des problèmes de contentViewdimensionnement dans iOS 8. Il a tendance à être disposé très tard dans le cycle, ce qui peut provoquer des conflits de contraintes temporaires. Pour résoudre ce problème, j'ai ajouté la méthode suivante dans une catégorie 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
}

Cette méthode doit être appelée après avoir retiré la cellule de la file d'attente.

Phatmann
la source
Ce serait idéal si cette méthode était appelée automatiquement. Je n'aime pas ça, mais cela pourrait être fait en balançant dequeueReusableCellWithReuseIdentifier:forIndexPath:.
phatmann
Apple semble avoir résolu ce problème dans la version 8.1 du SDK iOS dans Xcode 6.1 GM (Build 6A1042b). Par conséquent, j'ai mis à jour le code ci-dessus pour ne pas s'exécuter lors de l'utilisation du SDK 8.1. Une fois que votre équipe est passée à Xcode 6.1, vous pouvez supprimer complètement ce hack.
phatmann
1

J'ai résolu de faire ceci:

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

voir: ici

Serluca
la source
-1

Assurez-vous simplement de cocher la case «Autoresize subviews» dans la pointe de cette cellule de vue de collection. Cela fonctionnera bien sur iOS 8 et iOS 7.

Deepak GM
la source
Non, ce n'est pas le cas. Il s'agit d'une cellule prototype intégrée.
thkeen le