Problème de mise en page automatique avec un en-tête de section UITableView
Je travaille avec un UITableViewController
. J'ai un tableau d'objets que l'utilisateur peut supprimer si il va en modifier d'autres. Quand il passe en mode edit, je veux afficher un en-tête qui donne une option pour supprimer tous les éléments. Dans le même temps, il doit afficher une étiquette donnant des informations sur la quantité d'espace utilisé. Je veux ce pour redimensionner automatiquement si l'appareil passe en mode paysage. À partir de ce que je peux dire, j'ai besoin d'utiliser la mise en page automatique pour ce faire.
J'aurais aimé configurer l'en-tête dans un UIView
conçu dans le Storyboard, mais le Storyboard permet uniquement de visualiser les contrôleurs, pas vues. Je sais que je pourrais avoir un fichier XIB tenir, mais je préfère éviter que si je le pouvais.
Pour commencer, j'ai remplacé la editing
de la propriété, de sorte que je peux redessiner le tableau de la section lorsqu'en mode d'édition.
- (void)setEditing:(BOOL)editing animated:(BOOL)animated
{
[super setEditing:editing animated:animated];
NSIndexSet *set = [NSIndexSet indexSetWithIndex:0];
[self.tableView reloadSections:set withRowAnimation:UITableViewRowAnimationAutomatic];
}
- Je utiliser ce code à insérer l'en-tête de section, le cas échéant:
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
if (self.isEditing)
return [self headerView];
else
return nil;
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
if (self.isEditing)
return [self headerView].frame.size.height;
else
return 0;
}
La magie qui se passe dans le - headerView
méthode. Elle renvoie une UIView *
l'obtenir à partir d'une cache si nécessaire. Il ajoute un bouton et de l'étiquette, puis se met dans la les contraintes. J'ai utilisé ces mêmes contraintes dans le Storyboard et je n'ai pas eu de problèmes.
- (UIView *)headerView
{
if (headerView)
return headerView;
float w = [[UIScreen mainScreen] bounds].size.width;
UIButton *deleteAllButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[deleteAllButton setTitle:@"Delete All" forState:UIControlStateNormal];
CGRect deleteAllButtonFrame = CGRectMake(8.0, 8.0, 30.0, 30); //The autolayout should resize this.
[deleteAllButton setFrame:deleteAllButtonFrame];
deleteAllButton.translatesAutoresizingMaskIntoConstraints = NO;
[deleteAllButton setContentHuggingPriority:252 forAxis:UILayoutConstraintAxisHorizontal];
[deleteAllButton setContentCompressionResistancePriority:751 forAxis:UILayoutConstraintAxisHorizontal];
CGRect textFrame = CGRectMake(47.0, 8.0, 30.0, 30); //The autolayout should resize this.
UILabel *currSizeText = [[UILabel alloc] initWithFrame:textFrame];
currSizeText.text = @"You have a lot of text here telling you that you have stuff to delete";
currSizeText.translatesAutoresizingMaskIntoConstraints = NO;
currSizeText.adjustsFontSizeToFitWidth = YES;
CGRect headerViewFrame = CGRectMake(0, 0, w, 48);
headerView = [[UIView alloc] initWithFrame:headerViewFrame];
//headerView.autoresizingMask = UIViewAutoresizingNone;//UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
//headerView.translatesAutoresizingMaskIntoConstraints = NO;
[headerView addSubview:deleteAllButton];
[headerView addSubview:currSizeText];
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(deleteAllButton, currSizeText);
[headerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"|-[deleteAllButton]-[currSizeText]-|"
options:0
metrics:nil
views:viewsDictionary]];
[headerView addConstraint:[NSLayoutConstraint constraintWithItem:deleteAllButton
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:headerView
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];
[headerView addConstraint:[NSLayoutConstraint constraintWithItem:currSizeText
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:headerView
attribute:NSLayoutAttributeHeight
multiplier:0.5
constant:0]];
return headerView;
}
Droit maintenant, tout fonctionne à merveille. Le bouton conserve une taille constante (parce que la tendresse et de la résistance à la compression sont plus élevés que le label) et le label va modifier son texte pour l'adapter à l'espace disponible. Il redimensionne quand j'ai faites pivoter l'appareil. Le centrage vertical semble désactivé sur l'étiquette, mais je suis prêt à oublier pour l'instant.
Cependant, lorsque j'ai d'abord configuration de l'en-tête de section, je reçois un ennuyeux de mise en page automatique d'avertissement.
2014-02-07 11:25:19.770 ErikApp[10704:70b] 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:0xb9a4ad0 H:|-(NSSpace(20))-[UIButton:0xb99e220] (Names: '|':UIView:0xb9a4680 )>",
"<NSLayoutConstraint:0xb9a4bf0 H:[UIButton:0xb99e220]-(NSSpace(8))-[UILabel:0xb99f530]>",
"<NSLayoutConstraint:0xb9a4c20 H:[UILabel:0xb99f530]-(NSSpace(20))-| (Names: '|':UIView:0xb9a4680 )>",
"<NSAutoresizingMaskLayoutConstraint:0xa2d1680 h=--& v=--& H:[UIView:0xb9a4680(0)]>"
)
Will attempt to recover by breaking constraint
<NSLayoutConstraint:0xb9a4bf0 H:[UIButton:0xb99e220]-(NSSpace(8))-[UILabel:0xb99f530]>
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.
Ma première pensée a été de changer le retourné UIView
propriété translatesAutoresizingMaskIntoConstraints
à NO
. Quand je fais ça, j'ai eu un crash au lieu d'un avertissement. Pas exactement une amélioration.
2014-02-07 10:49:13.041 ErikApp[10597:70b] *** Assertion failure in -[UITableView layoutSublayersOfLayer:], /SourceCache/UIKit_Sim/UIKit-2903.23/UIView.m:8540
2014-02-07 10:49:13.383 ErikApp[10597:70b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Auto Layout still required after executing -layoutSubviews. UITableView's implementation of -layoutSubviews needs to call super.'
Quelqu'un a une suggestion que faire pour se débarrasser de l'avertissement?
source d'informationauteur Erik Allen
Vous devez vous connecter pour publier un commentaire.
Il semble que, lorsque votre article est de rechargement, la UITableView à un certain moment a une référence à l'ancien en-tête de section et le nouveau. Et si c'est le même point de vue, certains problèmes apparaissent. Donc, vous devez toujours fournir un point de vue différent de la
tableView:viewForHeaderInSection:
méthode.Parfois, il est vraiment utile d'avoir une seule instance pour être présenté dans un en-tête de section. Pour ce faire, vous devez créer un nouveau point de vue chaque fois que vous êtes invité pour un en-tête de section et de mettre votre affichage personnalisé à l'intérieur, la configuration de contraintes de manière appropriée. Voici un exemple:
J'ai créé un dépôt GitHub pour ce post ici:https://github.com/bilobatum/AnimatedTableHeaderDemo
Cette solution met en oeuvre un tableau d'en-tête de la vue, c'est à dire,
self.tableView.tableHeaderView
au lieu de les en-têtes de section pour une vue de la table avec une seule section.La table d'en-tête de la vue et de ses sous-vues sont de couleur pour des fins de test. L'arbitraire d'un en-tête de table hauteur est choisie pour des fins de test.
La table d'en-tête est paresseusement instancié et anime en place lors de l'affichage de la table entre dans le mode d'édition. Une animation se cache la table d'en-tête lors de l'affichage de la table quitte le mode d'édition.
En général, vous n'êtes pas censé ensemble d'images lors de l'utilisation de l'Auto Mise en page. Cependant, un tableau d'en-tête est un cas particulier, dans un sens. N'utilisez pas de Mise en page Automatique de la taille ou de la position d'un en-tête de table. Au lieu de cela, vous devez définir un tableau d'en-tête de l'image (en fait, vous avez seulement besoin de définir le rectangle de hauteur). À son tour, le système sera de traduire la table d'en-tête de l'image dans les contraintes.
Cependant, il est correct d'utiliser la Mise en page Automatique sur la table d'en-tête du sous-vues. Certaines de ces contraintes sont installés sur la table d'en-tête de la vue.
Tout à fait un moment depuis que vous avez posé la question, mais peut-être que la réponse est jet serviable pour vous (ou d'autres).
Mise en page automatique est automatiquement ajouté une contrainte pour l'ensemble de la section en-tête de la largeur (la dernière de la sortie de débogage contraint la liste). Cela devrait bien sûr pas de problème, comme la largeur est prise en compte lors du calcul de la cadres de la sous-vues.
Mais parfois, il semble y avoir des erreurs d'arrondi dans le calcul des images...
Juste ajouter une moindre priorité à l'un des sous-vues largeur de valeurs pour résoudre le problème:
...@"|-[deleteAllButton(30.0@999)]-[currSizeText]-|"
Si la largeur de bouton n'est pas l'usage constant ...deleteAllButton(>=30@999)...
La solution de contournement que j'ai essayé d'utiliser est de passer de l'en-tête de section trucs et aller directement à la
tableHeaderView
. J'ai remplacé monediting
propriété avec ceci:Il n'a pas d'animer en tant que bien que l'en-tête de section, mais cela va faire pour l'instant.
Ce n'est pas vraiment aborder le vrai problème (d'où l'expression "solution de contournement") donc je ne vais pas accepter cette solution.