web-dev-qa-db-fra.com

Problème d'auto-dimensionnement du cadre de UICollectionViewCell contentView dans la cellule prototype de Storyboard (Xcode 6, iOS 8 SDK) lors d'une exécution sur iOS 7 uniquement

J'utilise Xcode 6 Beta 3, iOS 8 SDK. Construire la cible iOS 7.0 en utilisant 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 contenant 1 étiquette au centre (pas de règle de redimensionnement automatique). L’arrière-plan violet consistait à marquer un contentView généré en exécution par la cellule, je suppose. Cette vue sera redimensionnée correctement en fonction de mon UICollectionViewLayoutDelegate, mais pas sous 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 bouton UIB 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 au cadre de (0,0,50,50) et ne sont plus jamais conformes à ma règle de décomposition automatique.

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

Je suppose que c'est un bogue dans iOS 8 SDK ou Swift ou peut-être Xcode?


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

Mise à jour 2: Collez ce code au début de votre cellForItem et tout ira bien:

/** 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 **/
158
thkeen

contentView est cassé. Il peut être également 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]
}
169
Igor Palaguta

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 sont dimensionnées par le biais de 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 un nib est codé dans iOS 8, puis décodé sous iOS 7, vous obtiendrez une vue du contenu sans masque de redimensionnement automatique et aucun autre moyen de se redimensionner lui-même. Par conséquent, si vous modifiez jamais le cadre de la cellule, la vue du contenu ne suivra pas.

Les applications en cours de déploiement sur iOS 7 devront résoudre ce problème en dimensionnant lui-même la vue du contenu, en ajoutant du masquage automatique ou en ajoutant des contraintes.

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

Comme je l’ai déjà dit, la solution de rechange que Daniel Plamann a décrite décrit pour moi des œuvres. Celles décrites par Igor Palaguta et KoCMoHaBTa semblent plus simples, et semblent logiques de donner une réponse à Apple DTS, je les essayerai plus tard.

61
ecotax

J'ai rencontré le même problème et j'espère que Apple résoudra ce problème avec la prochaine version de Xcode. En attendant, j'utilise une solution de contournement. Dans ma sous-classe UICollectionViewCell, je viens de remplacer layoutSubviews et de redimensionner le contenu. Afficher manuellement si la taille diffère de celle de collectionViewCell.

- (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;
  }
}
60
Daniel Plamann

Une autre solution consiste à définir la taille de contentView et à redimensionner automatiquement les masques dans -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 disposition automatique, car les masques de redimensionnement automatique sont convertis en contraintes.

38
KoCMoHaBTa

Dans Xcode 6.0.1, contentView pour UICollectionViewCell est cassé pour les périphériques 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)]];
6
SerJ_G

Cela ne fonctionnera pas correctement sans aucune des solutions de contournement mentionnées à cause 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 une certitude à 100% que cela soit lié à Xcode et ne concerne pas l'exécution, je suis très confiant - voici comment je peux le montrer:

  1. Construire + Exécuter l'application dans Xcode 5.1.
  2. Accédez au répertoire de l'application de simulateur et copiez le fichier .nib compilé pour le fichier xib avec lequel vous rencontrez des problèmes.
  3. Construire + Exécuter l'application dans Xcode 6 GM.
  4. Arrêtez l'application.
  5. Remplacez le fichier .nib du 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 cette .nib devrait fonctionner comme prévu.

J'espère que tous ceux qui liront cette question déposeront un radar avec Apple. C’est un problème ÉNORME qu’il faut régler avant la version finale de Xcode.

Edit: À la lumière de message de ecotax , je voulais juste mettre à jour cela pour dire que les différences de comportement entre la construction dans iOS 8 et iOS 7 sont maintenant confirmées, mais pas un bug. Mon piratage a corrigé le problème car la construction sur iOS 7 ajoutait le masque de redimensionnement automatique à la vue du contenu nécessaire pour que cela fonctionne, ce que Apple n'ajoute plus.

4
Acey

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

First Allez à votre UICollectionViewCell Subclass et collez le code suivant tel qu'il est dans la classe.

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

En passant, 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é.

4
onCompletion

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

Premièrement, il y a deux "règles":

  1. Pour les vues créées par programme (Ex. [UIView new]), la propriété translatesAutoresizingMaskIntoConstraints est définie sur YES.
  2. Les vues créées dans le générateur d'interface, avec la mise en forme automatique activée, auront la propriété translatesAutoresizingMaskIntoConstraints définie sur NO

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 de Storyboard, notez que la contentView de la cellule n’est pas exposée. Nous ne "contrôlons" pas la contentView, Apple.

Explorez le code source du storyboard et voyez comment la cellule contentView 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">

La contentView n'a pas comme valeur translatesAutoresizingMaskIntoConstraints sur NO. De plus, il manque de définition de la mise en page, peut-être à cause de ce que @ ecotax dit .

Si nous examinons la contentView, elle possède un masque de taille automatique, mais aucune définition ne le définit: <autoresizingMask key="autoresizingMask"/>

Donc, il y a deux conclusions:

  1. contentViewtranslatesAutoresizingMaskIntoConstraints est 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 la contentViewtranslatesAutoresizingMaskIntoConstraints sur NO dans awakeFromNib et définir des contraintes dans - (void)updateConstraints.

4
kgaidis

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

override var bounds: CGRect {
  didSet {
    // Fix autolayout constraints broken in Xcode 6 GM + iOS 7.1
    self.contentView.frame = bounds
  }
}
2
Ian

J'ai constaté qu'il existait également des problèmes avec le dimensionnement contentView dans iOS 8. Il a tendance à s'afficher très tard dans le cycle, ce qui peut provoquer des conflits temporaires de contraintes. 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 la mise en file d'attente de la cellule.

1
phatmann

J'ai fixé ceci:

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

voir: ici

1
Serluca