web-dev-qa-db-fra.com

"La mise en page automatique est toujours requise après l'exécution de -layoutSubviews" avec la sous-classe UITableViewCell

En utilisant XCode 4.5 et iOS 6, je développe une application avec un tableau simple avec des cellules personnalisées. Je l'ai fait cent fois dans iOS 5 et inférieur, mais pour une raison quelconque, le nouveau système autoLayout me cause beaucoup de problèmes.

J'ai configuré ma vue de table et ma cellule prototype dans IB, ajouté des sous-vues et les ai connectées en tant qu'IBOutlets, puis mon délégué et mon source de données. Cependant, maintenant, chaque fois que la première cellule est extraite de cellForRowAtIndexPath, j'obtiens l'erreur suivante:

*** Échec d'assertion dans - [ShopCell layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2372/UIView.m:5776

*** Fermeture de l'application en raison d'une exception non interceptée 'NSInternalInconsistencyException', raison: 'La mise en page automatique est toujours requise après l'exécution de -layoutSubviews. L'implémentation de -layoutSubviews par ShopCell doit appeler super. '

Je n'ai pas implémenté de méthode -layoutSubviews dans ma cellule sous-classée (ShopCell), et même lorsque j'essaie de le faire, j'ajoute le super appel car il suggère que je reçois toujours la même erreur. Si je supprime les sous-vues de la cellule dans IB et que je le change en UITableViewCell standard, tout fonctionne comme prévu, bien que je ne dispose évidemment d'aucune donnée dans mes cellules.

Je suis presque certain qu'il me manque quelque chose de simple, mais je ne trouve aucune documentation ou guide suggérant ce que j'ai fait de mal. Toute aide serait appréciée.

Edit: Je viens d'essayer de le changer en UITableViewCell dans IB et de laisser toutes les sous-vues en place, toujours la même erreur.

113
Mike Mayo

J'ai rencontré le même problème en ajoutant manuellement des contraintes dans le code. En code, je faisais ce qui suit:

{
    [self setTranslatesAutoresizingMaskIntoConstraints:YES];
    [self addSubview:someView];
    [self addSubview:someOtherView];
    [self addConstraint:...];
}

Hypothèse

D'après ce que je peux dire, le problème est que lorsque vous désactivez translatesAutoresizingMaskIntoConstraints, UITableViewCell commence à utiliser la mise en page automatique et échoue naturellement car l'implémentation sous-jacente de layoutSublayersForLayer n'appelle pas super. Quelqu'un avec Hopper ou un autre outil peut le confirmer. Puisque vous utilisez IB, vous vous demandez probablement pourquoi c'est un problème ... et c'est parce que l'utilisation de IB désactive automatiquement translatesAutoresizingMaskIntoConstraints pour les vues auxquelles elle ajoute des contraintes (elle ajoutera automatiquement une contrainte de largeur et de hauteur à leur place).

Solution

Ma solution a été de tout déplacer dans la variable contentView.

{
   [self.contentView addSubview:someView];
   [self.contentView addSubview:someOtherView];
   [self.contentView addConstraint:...];
}

Je ne suis pas sûr à 100% si cela fonctionnera dans Interface Builder, mais si vous poussez tout hors de votre cellule (en supposant que vous ayez quelque chose directement dessus), alors cela devrait fonctionner. J'espère que cela vous aide!

57
Malaxeur

Apparemment, l'implémentation layoutSubviews de UITableViewCell n'appelle pas super, ce qui pose un problème de mise en page automatique. Je serais intéressé de voir si la suppression de la catégorie ci-dessous dans les projets corrige les problèmes. Cela a aidé dans un projet test.

#import <objc/runtime.h>
#import <objc/message.h>

@implementation UITableViewCell (FixUITableViewCellAutolayoutIHope)

+ (void)load
{
    Method existing = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method new = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existing, new);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

J'ajouterais peut-être le problème qui se présentait pour moi lors de l'utilisation de backgroundView sur la cellule du tableau, car celui-ci est ajouté en tant que sous-vue à la cellule (alors que la plupart des sous-vues doivent être ajoutées à contentView de la cellule, ce qui devrait normalement mieux fonctionner).

Remarque: Il semble que ce bogue soit corrigé dans iOS7; J'ai pu supprimer ce code, ou au moins ajouter une vérification d'exécution, de sorte que cette opération ne soit effectuée que sur iOS6.

53
Carl Lindberg

J'ai eu le même bug pendant quelques mois. Mais j'ai trouvé quel était le problème.

Lorsque je crée un fichier IB, une UIView est déjà ajoutée. Si vous utilisez cette vue, l'application ne plante pas lorsque la mise en page automatique est désactivée (mais il y a d'autres problèmes). Lorsque vous utilisez la présentation automatique, vous devez sélectionner la vue right dans la bibliothèque d'objets: UITableViewCell.

En fait, vous devriez toujours utiliser cet élément car toutes les sous-vues sont ajoutées à la contentView de la UITableViewCell.

C'est tout. Tout ira bien.

33
Arnaud

J'ai eu le même problème avec la coutume UITableViewHeaderFooterView + xib.

J'ai vu quelques réponses ici, mais j'ai trouvé quelle implémentation -layoutSubviews dans mon problème de classe de vue de pied de page personnalisé corrige:

-(void)layoutSubviews
{
    [super layoutSubviews];
    [self layoutIfNeeded]; // this line is key
}
17
Sound Blaster

Avait le même problème dans iOS 7 (iOS 8 semble résoudre). La solution pour moi consistait à appeler [self.view layoutIfNeeded] à la fin de ma méthode viewDidLayoutSubviews.

15
Keller

Je voyais cela comme un résultat de la modification des contraintes dans mon implémentation de layoutSubviews. Déplacer l'appel vers super du début à la fin de la méthode a résolu le problème.

15
Phil Loden

J'ai eu le même problème. Le problème était dans la façon dont je créais la cellule Xib. J'ai créé un Xib comme d'habitude et viens de changer le type de "UIView" par défaut en ma classe UITableViewCell personnalisée. La méthode correcte consiste à supprimer d’abord la vue par défaut, puis à faire glisser l’objet de cellule de la vue tableau sur le fichier xib. Plus de détails ici: http://allplayers.github.io/blog/2012/11/18/Custom-UITableViewCell-With-NIB/

14
Trunal Bhanse

J'ai résolu le problème en désactivant "Autolayout" pour toutes les sous-vues de ma cellule d'affichage de table personnalisée.

Dans la bibliothèque xib d'une cellule personnalisée, sélectionnez une sous-vue et désélectionnez Inspecteur de fichier> Document du constructeur d'interface> Utiliser le report automatique

7
Johno

J'ai eu un problème similaire non pas sur UITableViewCell mais plutôt sur UITableView lui-même. Parce que c'est le premier résultat sur Google, je le poste ici. Il s’est avéré que viewForHeaderInSection était le problème. J'ai créé un UITableViewHeaderFooterView et mis translatesAutoresizingMaskIntoConstraints à NO. Maintenant voici la partie intéressante:

IOS 7:

// don't do this on iOS 7
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

Si je fais cela l'application se bloque avec

La mise en page automatique est toujours requise après l'exécution de -layoutSubviews . L'implémentation de -layoutSubviews par UITableView doit appeler super.

OK, je pensais que vous ne pouviez pas utiliser la disposition automatique sur un en-tête de vue de tableau et uniquement sur les sous-vues. Mais ce n'est pas la vérité telle que vous la verrez plus tard. Pour résumer: ne désactivez pas le masque de redimensionnement automatique de l'en-tête sur iOS 7. Sinon, cela fonctionne correctement.

iOS 8:

// you have to do this, otherwise you get an auto layout error
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;

Si je ne l'utilisais pas, j'obtiendrais le résultat suivant:

Impossible de satisfaire simultanément aux contraintes.

Pour iOS 8, vous devez désactiver le masque de redimensionnement automatique de l'en-tête.

Je ne sais pas pourquoi cela se comporte de cette manière, mais il semble qu'Apple ait corrigé certaines choses dans iOS 8 et que la mise en page automatique fonctionne différemment sous iOS 7 et iOS 8.

7
testing

Comme quelqu'un ci-dessus l'a déjà indiqué, lorsque vous créez une vue à utiliser dans une vue UITableView, vous devez supprimer la vue créée par défaut et faire glisser une vue UITableViewCell ou UITableViewHeaderFooterView en tant que vue racine. Cependant, il existe un moyen de réparer le XIB au cas où vous auriez manqué cette partie. Vous devez ouvrir le fichier XIB dans un éditeur de texte, dans la balise racine et dans son enfant direct, ajouter/modifier l'attribut translatesAutoresizingMaskIntoConstraints à YES, par exemple 

<view contentMode="scaleToFill" horizontalHuggingPriority="1000" id="1" translatesAutoresizingMaskIntoConstraints="YES" customClass="MXWCollapsibleTableHeaderView">

5

Ajoutez vos sous-vues à la contentView de la cellule au lieu de la cellule elle-même . Donc au lieu de:

[self addSubview:someView];

tu dois utiliser

[self.contentView addSubview:someView];

2

Je rencontre ceci et il semble que cela soit lié aux sous-classes UITableViewCell en tant que cellules prototypes auxquelles ont été ajoutées d'autres sous-classes UIView personnalisées. Je mets l'accent sur la «coutume» ici parce que j'ai réussi à utiliser des cellules qui n'ont que des enfants UIKit, mais cela échoue lorsque nous essayons de créer des contraintes pour les vues que j'ai créées sur mesure, en jetant l'erreur mentionnée dans la question de l'auteur.

J'ai dû séparer mes cellules en plumes indépendantes qui n'utilisent pas AutoLayout.

Espérons que Apple nettoie ce gâchis.

2
Lee Probert

J'ai rencontré celui-ci car j'avais initialement ajouté un UIView au lieu d'un UITableViewCell à un fichier xib.

1
mjmdavis

J'avais rencontré ce problème pour la première fois aujourd'hui. Jusqu'à présent, j'avais plusieurs expériences dans l'utilisation de prototypes de sous-classes UITableViewCell, mais je n'ai jamais rencontré ce problème. Ce qui était différent dans la cellule sur laquelle je travaillais, c’est que j’avais un IBOutlet à -backgroundView que j’utilisais pour colorer la cellule. J'ai découvert que, si je créais une nouvelle propriété et ajoutais encore une nouvelle UIView qui étendait la totalité de la cellule, cette affirmation disparaissait. Pour vérifier si cela était la cause, je suis revenu à attacher cette vue à la sortie backgroundView et l'assertion est réapparue. Jusqu'à présent, aucun autre problème d'utilisation d'AutoLayout dans un prototype UITableViewCell de sous-classe depuis que j'ai apporté cette modification.

1
Eric Schramm

Je n'ai pas trouvé de solution appropriée à ce problème, mais vous pouvez le résoudre en utilisant des cadres et en ne définissant pas la propriété translatesAutoresizingMaskIntoConstraints sur No (par défaut, c'est oui, ne le définissez pas)

CGRect headerViewFrame = CGRectMake(0,0,320,60); //Frame for your header/footer view
UIView *tableHeaderView = [[UIView alloc] initWithFrame:headerViewFrame];
tableHeaderView.translatesAutoresizingMaskIntoConstraints = Yes; //Or don't set it at all
[self.tableView setTableHeaderView:tableHeaderView];
1
sanjana

J'ai éliminé cette erreur en découplant le connecteur backgroundView de mes connecteurs UIImageView et accessoryView de fond, de mes personnalisations UIButton. Je soupçonne que ceux-ci ne sont pas destinés à être utilisés de la manière que je les utilisais.

1
Owen Godfrey

J'ai trouvé la solution.

Dans mon cas, j'ai créé la vue de la cellule dans le storyboard (avec la mise en page automatique activée) et j'ai défini l'interface UITableViewCell personnalisée dans ViewController.m. Je dois déplacer l'interface vers ViewController.h.

0
Nim Thitipariwat

J'ai modifié la réponse de Carl Lindberg pour remplacer plutôt UITableView et cela a commencé à fonctionner pour moi:

UITableView + AutoLayoutFix.h

@interface UITableView (AutoLayoutFix)
@end

UITableView + AutoLayoutFix.m

#import <objc/runtime.h>

@implementation UITableView (AutoLayoutFix)

+ (void)load
{
    Method existingMethod = class_getInstanceMethod(self, @selector(layoutSubviews));
    Method newMethod = class_getInstanceMethod(self, @selector(_autolayout_replacementLayoutSubviews));

    method_exchangeImplementations(existingMethod, newMethod);
}

- (void)_autolayout_replacementLayoutSubviews
{
    [super layoutSubviews];
    [self _autolayout_replacementLayoutSubviews]; // not recursive due to method swizzling
    [super layoutSubviews];
}

@end

Ensuite, dans MyViewController.m je viens d'importer la catégorie:

#import "UITableView+AutoLayoutFix.h"
0
plowman

J'ai rencontré le même problème et finalement trouvé la raison était que j'ai ajouté une contrainte à UITableViewCell, qui devrait être le contentView de UITableViewCell. Quand j'ai changé la contrainte, tout s'est bien passé! 

0
Alfy

J'ai rencontré le même problème lorsque j'utilise le storyboard pour créer le UITableViewCell personnalisé. Heureusement, j’ai trouvé le problème, car j’ai connecté l’accessoireView ([UITableViewCell setAccessoryView:]) à UIButton que j’ai ajouté à la cellule. 

Donc, cela s'est produit dans mon projet lorsqu'il était exécuté sur iOS6.

Solution

Je libère la prise entre accessoriesView et mon bouton qui contenait la cellule personnalisée.

Proposition

Vous ne devriez pas utiliser les éléments natifs de UITableViewCell et changer il.

0
jiwq

Le problème est la séquence des appels de mise en page dans les sous-vues:

Check-out

S'affiche dans iOS <8

0
Shanu ji

J'avais un problème très similaire avec une vue de pied de table que je configurais dans Xcode 6, iOS 7 + . La solution était au format du fichier nib. Apparemment, il était bloqué au format Xcode 4 ou quelque chose d'autre . Changer les paramètres du fichier pour "ouvre dans: Xcode 6.0" (ou par défaut, d'ailleurs), le corrige instantanément . La solution trouvée par hasard: c'est me rend fou, alors j'ai supprimé le fichier entier et créé à nouveau, évidemment avec les paramètres par défaut. Je ne sais pas pourquoi la simple modification du fichier dans le dernier Xcode ne l'a pas converti au format Xcode 5+, comme cela se produit habituellement.

f

0
user3099609

J'ai eu un problème similaire avec les cellules de vue de table statique dans IB. Une des cellules avait une sous-vue qui avait une classe qui avait été modifiée par erreur en une sous-classe de UITextfield. Le compilateur n'a donné aucun avertissement/erreur. Mais au moment de l'exécution, le système était incapable de charger le contrôleur de vue avec le crash susmentionné.

0
Yannick Winters

Je suis allé avoir le même problème. Je suis allé à mon DetailViewController et a renommé l'identifiant UIView. C'était auparavant sur UITableView. Cela a résolu le problème. Ce problème ne doit pas nécessairement figurer dans votre DetailViewController. Ce pourrait être dans n'importe quel autre. Essayez de le renommer avec l'identifiant respecté. 

0
RandomDude

J'ai eu exactement le même problème. Voici le problème avec mon projet:
Lorsque j’ai travaillé sur Interface Builder pour créer un UITableViewCell personnalisé, j’ai fait glisser un Vue au lieu d'une Cellule de vue de tableau depuis le volet de collection d'objets dans Xcode
en tant que cellule de tableau personnalisé.
Si vous êtes dans la même situation, voici la solution:
Supprimez la vue dans le générateur d’interface, veillez à faire glisser une cellule de la vue tableau à partir du volet de collection d’objets et à rétablir la vue de cellule de tableau personnalisée. Vous pouvez copier les objets de l'ancienne vue et les coller dans l'espace de travail de la nouvelle cellule de vue de tableau. 

0
us_david

Ce problème peut être causé par l'oubli d'appeler [super viewDidAppear:] dans viewDidAppear, mais je suis sûr que ce n'est pas la seule cause.

0
michaelsnowden

J'ai vécu la même chose. Il s'est avéré que si vous ajoutez par programme une sous-vue à partir de votre ShopCell .xib/storyboard, qui utilise la présentation automatique, en tant que sous-vue d'une autre vue, cette exception peut être levée, en fonction de la configuration de vos contraintes. Mon hypothèse est que ce sont les contraintes créées dans IB qui créent les problèmes lors de l'ajout par programme d'une vue en tant que sous-vue, puisqu'elles maintiennent ensuite les contraintes de viewA -> viewB. Vous pouvez également ajouter viewB en tant que sous-vue de viewC. Vous l'avez (cette phrase me confond même)?

Dans ma situation - étant donné que le problème était dû à des vues très simples -, j'ai créé les vues par programme et non dans IB. Cela l'a résolu. Vous pouvez extraire ces vues dans d’autres fichiers xib et désactiver la présentation automatique pour ceux-ci. Je suppose que ça marcherait.

0
Kasper Munck

Solution: Modifier les contraintes avant d'appeler super layoutSubviews

- (void)layoutSubviews
{

    [self _updateConstraints];

    [super layoutSubviews];
}
0
abuharsky

Dans mon cas,

Le UIImageView référencé pour la présentation automatique de UITableView est affecté à backgroundView de UITableView.

self.tableView.backgroundView = self.tableBackgroundImageView;

J'ai donc supprimé UIImageView for backgroundView de UIView (vue racine) et réinitialisé (supprimer) toutes les références de mise en page automatiques à cette UIImageView. J'ai placé UIImageView pour l'arrière-plan à l'extérieur de UIView (vue racine). Et puis assignez à la backgroundView de UITableView dans le code.

Puis corrigé.

0
Conan Kim

Dans certaines situations, cela résout facilement le problème de mise en page (en fonction de votre mise en page). Dans votre sous-classe UITableView, dans awakeFromNib ou init, définissez le masque de redimensionnement automatique:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Par défaut, il est défini sur UIViewAutoresizingNone

0
Arie Litovsky