Problème de contraintes de mise en page automatique sur iOS7 dans UITableViewCell

104

J'utilise des contraintes de mise en page automatique par programme pour mettre en page mes cellules UITableView personnalisées et je définis correctement les tailles de cellule dans tableView:heightForRowAtIndexPath:

Cela fonctionne très bien sur iOS6 et cela a l' air bien dans iOS7 aussi

MAIS lorsque j'exécute l'application sur iOS7, voici le type de message que je vois dans la console:

Break on objc_exception_throw to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.
2013-10-02 09:56:44.847 Vente-Exclusive[76306:a0b] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
        "<NSLayoutConstraint:0xac4c5f0 V:|-(15)-[UIImageView:0xac47f50]   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
        "<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>",
        "<NSLayoutConstraint:0xac43680 V:[UIView:0xac4d0f0(1)]>",
        "<NSLayoutConstraint:0xac436b0 V:[UIView:0xac4d0f0]-(0)-|   (Names: '|':UITableViewCellContentView:0xd93e850 )>",
        "<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0xac43650 V:[UIImageView:0xac47f50]-(>=0)-[UIView:0xac4d0f0]>

Et en effet, il y a une des contraintes dans cette liste que je ne veux pas:

"<NSAutoresizingMaskLayoutConstraint:0xac6b120 h=--& v=--& V:[UITableViewCellContentView:0xd93e850(44)]>"

et je ne peux pas définir la translatesAutoresizingMaskIntoConstraintspropriété de la contentViewsur NO => cela gâcherait toute la cellule.

44 est la hauteur de cellule par défaut, mais j'ai défini mes hauteurs personnalisées dans le délégué de vue tableau, alors pourquoi la cellule contentView a-t-elle cette contrainte? Qu'est-ce qui pourrait causer cela?

Dans iOS6, cela ne se produit pas et tout va bien sur iOS6 et iOS7.

Mon code est assez gros donc je ne le posterai pas ici mais n'hésitez pas à demander un pastebin si vous en avez besoin.

Pour spécifier comment je le fais, lors de l'initialisation de la cellule:

  • Je crée toutes mes étiquettes, boutons, etc.
  • J'ai défini leur translatesAutoresizingMaskIntoConstraintspropriété sur NON
  • Je les ajoute en tant que sous-vues contentViewde la cellule
  • J'ajoute les contraintes sur le contentView

Je suis également profondément intéressé à comprendre pourquoi cela ne se produit que sur iOS7.

Alexis
la source
Quelle est la hauteur de votre cellule personnalisée?
Mike Pollard
ma hauteur de cellule personnalisée est fixée à 90
Alexis
Un collègue a eu le même problème hier, mais avec une largeur par défaut de 320 (pour une application iPad).
jrturton
Il y a un exemple de projet ici démontrant ce même problème: github.com/Alex311/TableCellWithAutoLayout Voici quelques-unes de mes observations à ce sujet: github.com/Alex311/TableCellWithAutoLayout/commit/…
smileyborg
J'ai rencontré la même erreur, mais c'était parce que je n'ai jamais initialisé la cellule prototype supplémentaire que j'utilisais pour trouver la hauteur de ma cellule personnalisée.
Steve Moser

Réponses:

132

J'ai eu ce problème aussi. Il semble que le cadre de contentView ne soit pas mis à jour jusqu'à ce qu'il layoutSubviewssoit appelé, mais le cadre de la cellule est mis à jour plus tôt en laissant le cadre de contentView réglé {0, 0, 320, 44}au moment où les contraintes sont évaluées.

Après avoir examiné le contentView plus en détail, il semble que les masques de redimensionnement automatique ne soient plus définis.

La définition du masque autoresizing avant de contraindre vos vues peut résoudre ce problème:

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier
{
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdentifier];
    if (self)
    {
        self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
        [self loadViews];
        [self constrainViews];
    }
    return self;
}
liamnichols
la source
Cela fait le travail grâce à l'homme. Que pouvait-il se passer lors de l'utilisation de styles d'édition?
Alexis
11
@ L14M333 Voir le code que j'ai posté dans ce commentaire ici - en gros, vous devriez juste pouvoir définir self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);avant d'ajouter vos contraintes, ce qui devrait résoudre le problème.
smileyborg
2
le autoresizingMask ne fonctionne pas pour moi: la contrainte initiale a disparu mais les 3 nouveaux sont ajoutés "<NSAutoresizingMaskLayoutConstraint: 0x8b79740 h = - & - v = - & - UITableViewCellContentView: 0x8b44010.height == UITableViewCellScrollView: 0x8b4a1", "<NSAutoresizingMaskLayoutConstraint: 0x8b7b9f0 h = - & - v = - & - UITableViewCellScrollView: 0x8b4a130.height == LibraryCell: 0x8ac19d0.height>", "<NSAutoresizingMaskLayoutConstraint: 0x8b7cell = LibraryConstraint: 0x8b7c: 0x8ac19d0 (0)]> "
catamphétamine
1
Ça marche pour moi aussi, je me bats depuis une heure environ! Merci! <3
mokagio
2
Après m'être débarrassé d'autres problèmes de mise en page automatique (iOS 7 / iOS 8), j'ai découvert que l'utilisation self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);fonctionne aussi bien que l'utilisation self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;. J'ai mis cela updateConstraintsavant d'ajouter les contraintes à ma vue de contenu.
test
35

Apparemment, il y a quelque chose qui ne va pas avec UITableViewCell et UICollectionViewCell sur iOS 7 à l'aide du SDK iOS 8.

Vous pouvez mettre à jour le contentView de la cellule lorsque la cellule est réutilisée comme ceci:

Pour UITableViewController statique:

#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [super tableView:tableView cellForRowAtIndexPath:indexPath];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }

    //your code goes here

    return cell;
}

#endif
#endif

Étant donné que les contrôleurs de vue de table statique sont fragiles et peuvent facilement être cassés si vous implémentez des méthodes de source de données ou de suppression de données, il existe des vérifications qui garantiront que ce code ne sera compilé et exécuté que sur iOS 7

Il est similaire pour UITableViewController dynamique standard:

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *cellID = @"CellID";

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];

    if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1)
    {
        cell.contentView.frame = cell.bounds;
        cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleRightMargin |UIViewAutoresizingFlexibleTopMargin |UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleBottomMargin;
    }
    //your code goes here       
    return cell;
}

Dans ce cas, nous n'avons pas besoin de la vérification supplémentaire de compilation, car l'implémentation de cette méthode est requise.

L'idée est la même pour les deux cas et pour UICollectionViewCell, comme commenté dans ce fil: 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

KoCMoHaBTa
la source
1
C'est la meilleure réponse, ou du moins la meilleure solution pour moi. My Cell (règles de mise en page automatique) fonctionnait très bien dans iOS8 avec XCode6 et fonctionnait très bien avec iOS7 et 6 avec XCode 5. J'ai mis à jour XCode vers XCode6 et cela ne fonctionne plus sous iOS6 et 7. Alors, merci !!!!
xarly le
Ce n'est plus un problème avec Xcode 6.1. De plus, n'utilisez pas NSFoundationVersionNumber, veuillez consulter nshipster.com/swift-system-version-checking
onmyway133
1
@ onmyway133: Pourquoi ce n'est plus un problème dans Xcode 6.1? J'ai toujours le problème même si j'utilise Xcode 6.1 et iOS 8.1 SDK. Le problème se produit uniquement sur iOS 7. La modification des limites ou la définition d'un masque de redimensionnement automatique résout le problème. Mais j'ai utilisé l'approche énoncée dans la réponse la plus votée ou dans les commentaires de celle-ci.
test
Oui, c'est la meilleure réponse basée sur mes 3 heures de recherche sur Google. D'autres «solutions» similaires ne fournissent pas la liste complète de cell.contentView.autoresizingMask. Seul celui-ci fonctionne pour mon projet iPad 7.1 créé dans Xcode 6.
Golden Thumb
13

Comme les cellules sont réutilisées et que la hauteur peut changer en fonction du contenu, je pense qu'en général, il serait préférable de définir la priorité des espacements à moins que nécessaire.

Dans votre cas, UIImageView a un espacement de 15 vers le haut et la vue de dessous a un espacement de 0 vers le bas. Si vous définissez la priorité de ces contraintes sur 999 (au lieu de 1000), l'application ne plantera pas, car les contraintes ne sont pas obligatoires.

Une fois la méthode layoutSubviews appelée, la cellule aura la bonne hauteur et les contraintes peuvent être satisfaites normalement.

Steven
la source
Tu es un génie! solution agréable et propre merci :)
Blacky
9

Je n'ai toujours pas trouvé de bonne solution pour les storyboards ... Quelques infos aussi ici: https://github.com/Alex311/TableCellWithAutoLayout/commit/bde387b27e33605eeac3465475d2f2ff9775f163#commitcomment-4633188

D'après ce qu'ils y conseillent:

self.contentView.bounds = CGRectMake(0, 0, 99999, 99999);

J'ai induit ma solution:

  • faites un clic droit sur le storyboard
  • Ouvrir en tant que -> Code source
  • recherchez la chaîne "44" ici
  • ça va être comme

.

<tableView hidden="YES" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="44" ...
    <tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ChatMessageCell" id="bCG-aU-ivE" customClass="ChatMessageCell">
        <rect key="frame" x="0.0" y="22" width="320" height="44"/>

.

  • remplacez rowHeight = "44" par rowHeight = "9999" et height = "44" par height = "9999"
  • faites un clic droit sur le storyboard
  • Ouvrir en tant que -> Générateur d'interface
  • exécutez votre application et vérifiez la sortie
catamphétamine
la source
Cela a fonctionné pour moi. J'avais créé un UITableViewController dans le storyboard où certaines cellules ont été créées à partir d'un prototype et d'autres entièrement en code en instanciant une classe.
Atharva le
3

Augmentez simplement la hauteur de cellule par défaut. Il semble que le problème est que le contenu de la cellule est plus grand que la taille de cellule par défaut (initiale), violant certaines contraintes de non-négativité jusqu'à ce que la cellule soit redimensionnée à sa taille réelle.

Vadim Yelagin
la source
En effet, mes cellules sont plus hautes que la taille par défaut, mais comment se fait-il que la taille par défaut soit utilisée puisque le délégué de tableview implémente tableView: heightForRowAtIndexPath:?
Alexis
Les cellules sont probablement créées avec la taille par défaut, puis redimensionnées à la taille réelle.
Vadim Yelagin
en effet, cela fait le travail, mais pourquoi cela ne se produit-il pas sur iOS6 alors?
Alexis
3
@jafar iOS 7 a changé beaucoup de choses autour des cellules de vue de tableau. Sur iOS 7, il y a maintenant une vue de défilement (de type UITableViewCellScrollView) entre la cellule de vue de tableau et le contentView; cela explique probablement la différence entre iOS 6 et 7 ici.
smileyborg
3

Peut-être définir la priorité de la vue sur 750 et moins de 1000 peut résoudre.

"<NSLayoutConstraint:0xac43620 V:[UIImageView:0xac47f50(62)]>",
heramerom
la source
2

J'ai eu le même problème. Ma solution basée sur d'autres ci-dessus:

- (id)initWithCoder:(NSCoder *)aDecoder {
    self = [super initWithCoder:aDecoder];
    if (self) {
        // initialize my stuff
        [self layoutSubviews]; // avoid debugger warnings regarding constraint conflicts
    }
    return self;
}

Meilleur, Alessandro

Cormentis
la source
2
De la documentation d'Apple:You should not call this method directly. If you want to force a layout update, call the setNeedsLayout method instead to do so prior to the next drawing update. If you want to update the layout of your views immediately, call the layoutIfNeeded method.
Ricardo Sanchez-Saez
Je veux commencer une religion en te priant. J'ai cherché HOURS pour essayer de résoudre ce problème, et cela le résout (ainsi que la définition de contentView.bounds sur CGRectMake (0, 0, 99999, 99999);) - CECI EST UN BOGUE DANS LE CODE POMMES. Donc, je suis respectueusement en désaccord avec les documents de pommes disant de ne pas appeler cette fonction. Apple, corrige ta merde.
Ryan Copley
0

J'ai également rencontré ce problème et aucune des suggestions n'a aidé. Dans mon cas, j'avais une cellule de sélection de taille et elle contenait collectionView à l'intérieur (chaque cellule collectionView contenait une image en taille réelle). Maintenant, la taille de la cellule était un peu plus grande (60) que la collectionView (50) et les images à l'intérieur (50). En raison de cette vue collectionView a aligné le bas sur la contrainte superview avec une valeur de 10. Ce cas m'a averti, et la seule façon de résoudre ce problème était de rendre la hauteur de cellule identique à celle de collectionView.

Centurion
la source
0

J'ai fini par ne pas utiliser du tout UITableViewCell dans le concepteur ... Je crée une vue personnalisée et l'ajoute à la vue du contenu de la cellule par programmation ... Avec certaines catégories d'aide, il n'y a pas non plus de code standard ...

Renetik
la source
0

Si cela fonctionne correctement dans iOS8 et obtenir l'avertissement dans iOS7, vous pouvez rechercher le code source du storyboard et trouver la bonne tableviewCell, puis ajouter l'attribut rect après la ligne tableviewCell. Merci à kuchumovn .

<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" .../>
                                <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
NSDeveloper
la source