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

115

En utilisant XCode 4.5 et iOS 6, je développe une application avec une vue de tableau simple avec des cellules personnalisées. Je l'ai fait cent fois dans iOS 5 et versions antérieures, mais pour une raison quelconque, le nouveau système de mise en page automatique me pose beaucoup de problèmes.

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

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

*** Fin de l'application en raison d'une exception non interceptée «NSInternalInconsistencyException», raison: «Mise en page automatique toujours requise après l'exécution de -layoutSubviews. La mise en œuvre 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 et d'ajouter le super appel car il suggère que j'obtiens toujours la même erreur. Si je supprime les sous-vues de la cellule dans IB et que je la change en UITableViewCell standard, tout fonctionne comme prévu, même si bien sûr je ne me retrouve pas de données dans mes cellules.

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

Edit: J'ai juste essayé de le changer en UITableViewCell dans IB et de laisser toutes les sous-vues en place, toujours la même erreur.

Mike Mayo
la source
Essayez lldb [[UIWindow keyWindow] _autoLayoutTrace]dans la zone de débogage si la mise en page automatique est utilisée.
A-Live du
3
Utilisez-vous UIView pour la cellule personnalisée au lieu de UITableViewCell? J'ai eu le même problème. J'avais UIView pour la cellule personnalisée et j'y ajoutais des sous-vues. Changé en UITableViewCell et cela a fonctionné.
Hey Mike, comment définissez-vous les points de vente? Sont-ils des propriétés dans votre fichier d'implémentation dans une extension de classe?
kocodude
@ A-Live Chaque fois que j'essaye d'utiliser cette méthode, j'obtiens une erreur dans le débogueur .... cette méthode est-elle toujours valide? Edit: Nevermind, c'est un l minuscule dans la mise en page automatique.
borrrden
décochez la case autoLayout dans l'inspecteur puis nettoyez et exécutez. cela fonctionnera sûrement.
Nico

Réponses:

57

J'ai rencontré le même problème en ajoutant manuellement des contraintes dans le code. Dans le 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 disposition automatique et échoue naturellement car l'implémentation sous-jacente de layoutSublayersForLayern'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 d'IB désactive automatiquement les translatesAutoresizingMaskIntoConstraintsvues auxquelles il ajoute des contraintes (cela ajoutera automatiquement une contrainte de largeur et de hauteur à leur place).

Solution

Ma solution était de tout déplacer vers le 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 avez quelque chose directement dessus), cela devrait fonctionner. J'espère que cela vous aide!

Malaxeur
la source
4
J'avais également besoin d'ajouter subview.translatesAutoresizingMaskIntoConstraints = NO'chaque sous-vue que j'ajoutais au contentView.
Jay Peyer
5
Cela a fonctionné pour moi. Assurez-vous également de ne pas appeler self.contentView.translatesAutoresizingMaskIntoConstraints = NOpour le UITableViewCell.
Maurizio
53

Apparemment, l'implémentation layoutSubviews de UITableViewCell n'appelle pas super, ce qui est un problème avec la mise en page automatique. Je serais intéressé de voir si le fait de déposer la catégorie ci-dessous dans les projets corrige les choses. Cela a aidé dans un projet de 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

Je pourrais ajouter le problème qui m'est apparu lors de l'utilisation d'un backgroundView sur la cellule du tableau, car cela est ajouté en tant que sous-vue à la cellule (alors que la plupart des sous-vues devraient être ajoutées au contentView de la cellule du tableau, qui devrait généralement 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 pour qu'elle ne soit effectuée que si elle fonctionne sur iOS6.

Carl Lindberg
la source
La chose étrange est que cela fonctionne bien avec un UITableViewCell simple pour moi, mais pas pour une sous-classe ...
borrrden
Vous penseriez cela, mais je n'ai qu'une méthode init, rien d'autre> <. Je n'ai jamais remplacé les mises en page de ma vie, haha. Je pense que le problème est la vue personnalisée qu'UITableViewCell utilise comme vue racine ne peut pas utiliser la mise en page automatique car elle remplace layoutSubviews (donc lorsque vous essayez d'ajouter des contraintes à la vue racine, cela échouera)
borrrden
6
J'ai dû créer une telle catégorie UITableViewpour la même raison (iOS 6.1 b1)
Joshua J. McKinnon
5
Existe-t-il un correctif similaire pour TableHeaderView car le problème existe toujours dans iOS 7?
Softlion
1
Cela fonctionne très bien. J'ai rencontré ce problème lorsque j'ai tenté de centrer une sous-vue UIVIew dans un UITableView. Même dans iOS 7, l'assertion se produit. Mais cela ne se produit pas dans iOS 8, ils doivent donc avoir résolu le bogue.
Jordan H
33

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, un UIViewest déjà ajouté. Si vous utilisez cette vue, l'application ne plante pas lorsque la disposition automatique est désactivée (mais il y a d'autres problèmes). Lorsque vous utilisez la mise en page automatique, vous devez sélectionner la bonne vue dans la bibliothèque d'objets: UITableViewCell.

En fait, vous devriez toujours utiliser cet élément parce que tous les sous - vues sont ajoutés au contentViewdu UITableViewCell.

C'est tout. Tout ira bien.

Arnaud
la source
Cela ne devrait pas être la réponse acceptée car la question ne concerne pas une implémentation utilisant IB et parce que ce problème peut survenir lorsque vous n'utilisez pas IB. Si vous faites vos vues par programmation, la réponse de @ PhilLoden est plus viable.
Eric
Je n'ai pas compris la réponse. quelqu'un peut-il expliquer plus clairement? merci
hasan
Je pense que j'ai ce droit. suffit-il de vérifier la classe att. dans l'inspecteur d'identité dans le constructeur d'interface? ou celui qui a été ajouté était d'un autre type et classe att. a été mis à jour plus tard? cela pose-t-il également le problème?
hasan
@ hasan83 Vous pouvez en fait renvoyer la cellule. Un UITableViewCell est essentiellement un UIView avec un identifiant de réutilisation.
Arnaud
17

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

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

-(void)layoutSubviews
{
    [super layoutSubviews];
    [self layoutIfNeeded]; // this line is key
}
Sound Blaster
la source
1
Attention, cela peut entraîner une boucle sans fin et enfin EXC_BAD_ACCESS KERN_PROTECTION_FAILURE
mbi
15

Je voyais cela à la suite de la modification des contraintes dans ma mise en œuvre de layoutSubviews. Le déplacement de l'appel vers super du début à la fin de la méthode a résolu le problème.

Phil Loden
la source
Cela a fonctionné pour moi. J'ai un UICollectionViewCell personnalisé que je formate dans layoutSubviews. Quelqu'un sait pourquoi cette solution fonctionne?
STANGMMX
@STANGMMX, la réponse d'A'sa Dickens explique pourquoi.
Fábio Oliveira
15

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

Keller
la source
Je vous remercie. Cela m'aide, j'ai rencontré ce problème hier (sur iOS 7). cela aide pour iOS 7.
Alexander
@MaciejSwic voir ma réponse en haut.
Sound Blaster
Cela a fonctionné pour moi! Utilisation d'iOS 7.1 avec Swift. J'étais en train de supprimer et d'ajouter une contrainte sur viewDidLayoutSubviews. J'ai supprimé le super appel et cela ne fonctionnait toujours pas, mais cette solution a fait l'affaire! donnez une feuille à ce dinosaure! :)
jomafer
J'ai aussi travaillé pour moi avec iOS 7.1!
fdlr
14

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 normal et j'ai juste changé 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 vue tableau sur le xib. Plus de détails ici: http://allplayers.github.io/blog/2012/11/18/Custom-UITableViewCell-With-NIB/

Trunal Bhanse
la source
1
Un aperçu parfait! Je prendrais des années pour m'en rendre compte, surtout parce que mes applications ne planteraient pas si j'utilisais un UIView avec la mise en page automatique désactivée.
Guilherme
Super! voir aussi @Arnaud respone ci
Lubbo
7

J'ai résolu le problème en désactivant "Mise en page automatique" pour toutes les sous-vues de ma cellule de vue tableau personnalisée.

Dans le xib pour une cellule personnalisée, sélectionnez une sous-vue et décochez Inspecteur de fichiers> Document Interface Builder> Utiliser la mise en page automatique

Johno
la source
4
J'ai fait la même chose. Ce n'est pas vraiment une solution si vous souhaitez utiliser la mise en page automatique
ajmccall
7

J'ai eu un problème similaire non pas sur UITableViewCellmais plutôt sur UITableViewlui-même. Parce que c'est le premier résultat sur Google, je le posterai ici. Il s'est avéré que viewForHeaderInSectionc'était le problème. J'ai créé un UITableViewHeaderFooterViewet 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 plante avec

Mise en page automatique 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 pouvez pas utiliser la mise en page automatique sur un en-tête de vue tableau et uniquement sur les sous-vues. Mais ce n'est pas toute la vérité telle que vous la verrez plus tard. Pour résumer: ne désactivez pas le masque de redimensionnement automatique pour l'en-tête sur iOS 7. Sinon, cela fonctionne bien.

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 la sortie suivante:

Impossible de satisfaire simultanément les contraintes.

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

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

essai
la source
Mate tu viens de sauver ma journée!
Marcin Małysz
5

Comme quelqu'un ci-dessus l'a déjà indiqué, lorsque vous créez une vue à utiliser dans un UITableView, vous devez supprimer la vue créée par défaut et faire glisser un 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 et dans la balise racine et son enfant direct ajouter / modifier l'attribut translatesAutoresizingMaskIntoConstraintsen YES, par exemple

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

Maksymilian Wojakowski
la source
2

Je rencontre cela et il semble que cela soit lié aux sous-classes UITableViewCell en tant que cellules prototypes auxquelles sont spécifiquement ajoutées d'autres sous-classes UIView personnalisées. J'insiste ici sur la `` coutume '' parce que j'ai réussi avec des cellules qui n'ont que des enfants UIKit, mais cela tombe en essayant de créer les contraintes pour les vues que j'ai créées sur mesure, en lançant l'erreur indiquée dans la question des auteurs.

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

Espérons qu'Apple nettoie ce gâchis.

Lee Probert
la source
2

Ajoutez vos sous-vues au 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];

Christian Aberger
la source
1

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

mjmdavis
la source
1

J'ai éliminé cette erreur en découplant le backgroundViewconnecteur de mon arrière UIImageView- plan et le accessoryViewconnecteur de mes UIButtonpersonnalisations. Je soupçonne qu'ils n'étaient pas destinés à être utilisés comme je les utilisais.

Owen Godfrey
la source
1

J'avais rencontré ce problème pour la première fois aujourd'hui. Jusqu'à présent, j'avais une expérience variée dans l'utilisation des sous-classes prototypes de UITableViewCell, mais je n'ai jamais rencontré ce problème. Ce qui était différent dans la cellule avec laquelle je travaillais, c'était que j'avais un IBOutlet vers -backgroundView que j'utilisais pour colorer la cellule. J'ai trouvé que si je créais une nouvelle propriété et ajoutais toujours un nouveau UIView qui étirait la durée de la cellule entière, cette affirmation disparaissait. Pour vérifier que c'était la cause, je suis retourné à attacher cette vue à la sortie backgroundView et l'assertion est réapparue. Jusqu'à présent, aucun autre problème lors de l'utilisation de la mise en page automatique dans un prototype sous-classé UITableViewCell depuis que j'ai effectué cette modification.

Eric Schramm
la source
1

Je n'ai pas obtenu de solution appropriée pour ce problème, mais vous pouvez le résoudre en utilisant des cadres et en ne définissant pas la propriété translatesAutoresizingMaskIntoConstraints sur Non (par défaut, c'est oui, alors 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];
Sanjana
la source
0

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 mise en page automatique, en tant que sous-vue vers une autre vue, cette exception peut être levée, en fonction de la configuration de vos contraintes. Je suppose que les contraintes créées dans IB sont ce qui crée les problèmes lors de l'ajout par programme d'une vue en tant que sous-vue, car il s'agit alors de maintenir les contraintes de viewA -> viewB pendant ce temps, vous pouvez ajouter viewB comme sous-vue de viewC. Vous l'avez compris (cette phrase me confond même)?

Dans ma situation - puisque ce sont des vues très simples qui ont causé le problème - 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 disposition automatique pour ceux-ci. Je suppose que ça marcherait.

Kasper Munck
la source
0

Dans certaines situations, cela résout facilement le problème de mise en page (en fonction de votre mise en page). À l'intérieur de 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

Arie Litovsky
la source
Cela a résolu le problème auquel je faisais face. J'utilise la mise en page automatique dans une cellule de tableau combinée avec [tableViewCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].heightpour obtenir la hauteur, que j'utilise ensuite heightForRowAtIndexPath.
NathanAldenSr
0

Dans mon cas,

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

self.tableView.backgroundView = self.tableBackgroundImageView;

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

Puis réparé.

Conan Kim
la source
0

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 mon ViewController.m, je dois déplacer l'interface vers ViewController.h.

Nim Thitipariwat
la source
0

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 je sort l'accessoireView ([UITableViewCell setAccessoryView:]) à UIButton que j'ai ajouté à la cellule.

Cela s'est donc produit dans mon projet lors de l'exécution sur iOS6.

Solution

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

Proposition

Vous ne devez pas utiliser les éléments natifs de UITableViewCell et les modifier.

Wanqiang Ji
la source
0

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

MichaelNowden
la source
0

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

us_david
la source
0

J'ai eu un problème très similaire avec une vue de pied de page de table que je définissais dans Xcode 6, iOS 7+. La solution était au format du fichier nib. Apparemment, il était coincé au format Xcode 4 ou quelque chose comme ça. Changer les paramètres du fichier en "ouvre dans: Xcode 6.0" (ou par défaut, d'ailleurs), l'a instantanément résolu. J'ai trouvé la solution par hasard: cela me rendait fou, j'ai donc supprimé le fichier entier et créé à nouveau, évidemment avec les paramètres par défaut. Je ne sais pas pourquoi le simple fait de modifier le fichier dans le dernier Xcode ne l'a pas converti au format Xcode 5+, comme cela se produit généralement.

F

utilisateur3099609
la source
0

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

RandomDude
la source
0

J'ai eu un problème similaire avec les cellules de vue de tableau statique dans IB. Une des cellules avait une sous-vue qui avait une classe qui a é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 n'a pas pu charger le contrôleur de vue avec le crash susmentionné en conséquence.

Yannick Winters
la source
0

Le problème est le séquencement des appels de mise en page aux sous-vues:

Check-out

Apparaît dans iOS <8

Shanu ji
la source
0

Solution: modifier les contraintes avant d'appeler le super layout

- (void)layoutSubviews
{

    [self _updateConstraints];

    [super layoutSubviews];
}
abuharsky
la source
0

J'ai modifié la réponse de Carl Lindberg pour remplacer à la UITableViewplace 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, MyViewController.mje viens d'importer la catégorie:

#import "UITableView+AutoLayoutFix.h"
laboureur
la source
0

J'ai rencontré le même problème et j'ai finalement trouvé que 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é!

Alfy
la source