J'expérimente comment utiliser UIScrollView. Après bien des ennuis, j'ai finalement compris. Mais maintenant, il me semble que je frappe un autre problème.
Dans cette application simple, j'ai une vue de défilement avec et pour que cela fonctionne, je dois définir l'espace inférieur de la vue pour faire défiler la contrainte de vue à 0 comme décrit ici et cela fonctionne très bien. Je le fais par l'IB.
Maintenant, je suis tombé sur un scénario où je dois faire cette partie par programme. J'ai ajouté le code ci-dessous dans la méthode viewDidLoad
.
NSLayoutConstraint *bottomSpaceConstraint = [NSLayoutConstraint constraintWithItem:self.view
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:self.scrollView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:0.0];
[self.view addConstraint:bottomSpaceConstraint];
Mais cela ne semble pas fonctionner. Il affiche le message suivant dans la fenêtre de la console et je ne sais pas quoi en faire.
Impossible de satisfaire simultanément les contraintes. Probablement au moins une des contraintes dans la liste suivante est celle que vous ne voulez pas. Essayez ceci: (1) examinez chaque contrainte et essayez de déterminer celle à laquelle vous ne vous attendez pas; (2) recherchez le code qui a ajouté la ou les contraintes indésirables et corrigez-le. (Remarque: si vous voyez NSAutoresizingMaskLayoutConstraints que vous ne comprenez pas, reportez-vous à la documentation de la propriété UIView translatesAutoresizingMaskIntoConstraints) ("", "")
Est-ce que quelqu'un pourrait m'expliquer comment faire cela? J'ai également joint un projet de démonstration ici afin que vous puissiez avoir une meilleure idée du problème.
MISE À JOUR:
Tout d'abord merci pour les réponses. En utilisant les moyens mentionnés dans les réponses, j'ai pu le faire fonctionner. Cependant, dans un scénario légèrement différent, ce n'est pas le cas. Maintenant, j'essaie de charger une vue sur un viewcontroller par programme.
Si je peux m'expliquer davantage. Il y a 2 contrôleurs de vue. Le premier étant un UITableViewController
et le second un UIViewController
. A l'intérieur, un UIScrollView
. Il existe également plusieurs UIView
et la hauteur de certaines de ces vues dépasse la hauteur normale de l'écran.
UITableViewController
affiche une liste d'options. En fonction de la sélection de l'utilisateur, un UIView
particulier du lot sera chargé dans le UIViewController
avec le UIScrollView
.
Dans ce scénario, la méthode ci-dessus ne fonctionne pas. Le défilement ne se produit pas. Dois-je faire quelque chose de différent puisque je charge la vue séparément?
J'ai téléchargé un projet de démonstration ici afin que vous puissiez le voir en action. Veuillez consulter le Email.xib
et sélectionnez E-mail dans la liste de la vue tabulaire.
Sur la base d'un examen de votre code, quelques commentaires:
Vous n'avez généralement pas besoin d'ajuster les contraintes lorsqu'une vue apparaît. Si vous vous trouvez en train de faire cela, cela signifie souvent que vous n'avez pas configuré correctement votre storyboard (ou du moins, pas efficacement). La seule fois où vous avez vraiment besoin de définir/créer des contraintes est soit (a) vous ajoutez des vues par programme (ce que je suggère est plus de travail que cela vaut); ou (b) vous devez effectuer un ajustement au moment de l'exécution des contraintes (voir troisième puce sous le point 3 ci-dessous).
Je ne veux pas y croire, mais votre projet avait un tas de code redondant. Par exemple.
Vous définissiez le cadre pour la vue de défilement, mais cela est régi par des contraintes, de sorte que cela ne fait rien (c'est-à-dire lorsque les contraintes sont appliquées, tous les paramètres frame
définis manuellement seront remplacés). En général, dans la disposition automatique, n'essayez pas de changer directement frame
: modifiez les contraintes. Mais aucun changement de contraintes n'est nécessaire de toute façon, donc le point est sans objet.
Vous définissiez la taille du contenu pour la vue de défilement, mais dans la mise en page automatique, cela aussi est régi par des contraintes (des sous-vues), donc cela n'était pas nécessaire.
Vous définissiez des contraintes pour la vue de défilement (qui étaient déjà nulles), mais vous n'avez pas ajouté la vue de la NIB dans la vue de défilement, ce qui a également déjoué toute intention. La question initiale était de savoir comment modifier la contrainte inférieure de la vue de défilement. Mais la contrainte inférieure pour cela est déjà nulle, donc je ne vois aucune raison de la remettre à zéro.
Je suggère une simplification plus radicale de votre projet:
Vous vous rendez la vie beaucoup plus difficile en stockant vos opinions dans les NIB. C'est beaucoup plus facile si vous restez dans le monde du storyboard. Nous pouvons vous aider à faire les choses NIB si vous en avez vraiment besoin, mais pourquoi vous compliquer la vie?
Utilisez des prototypes de cellules pour faciliter la conception des cellules de votre tableau. Vous pouvez également définir les séquences pour passer des cellules à la scène suivante. Cela élimine le besoin d'écrire n'importe quel code didSelectRowAtIndexPath
ou prepareForSegue
. De toute évidence, si vous avez quelque chose que vous devez passer à la scène suivante, utilisez certainement prepareForSegue
, mais rien de ce que vous avez présenté jusqu'à présent ne l'exige, donc je l'ai commenté dans mes exemples.
En supposant que vous cherchiez un exemple pratique de contraintes changeantes par programme, j'ai configuré la scène de sorte que la vue texte change sa hauteur par programme, en fonction du texte dans la vue texte. Comme toujours, plutôt que de parcourir les contraintes pour trouver celle en question, lors de la modification d'une contrainte existante que IB a créée pour moi, je pense qu'il est beaucoup plus efficace de configurer un IBOutlet
pour la contrainte et d'éditer le constant
propriété de la contrainte directement, c'est donc ce que j'ai fait. J'ai donc configuré le contrôleur de vue pour être le délégué de la vue texte et j'ai écrit un textViewDidChange
qui a mis à jour la contrainte de hauteur de la vue texte:
#pragma mark - UITextViewDelegate
- (void)textViewDidChange:(UITextView *)textView
{
self.textViewHeightConstraint.constant = textView.contentSize.height;
[self.scrollView layoutIfNeeded];
}
Remarque, ma vue texte a deux contraintes de hauteur, une contrainte de hauteur minimale obligatoire et une contrainte de priorité moyenne que je modifie ci-dessus en fonction de la quantité de texte. Le point principal est qu'il illustre un exemple pratique de changement de contraintes par programme. Vous ne devriez pas du tout avoir à contourner la contrainte inférieure de scrollview, mais cela montre un exemple concret du moment où vous voudrez peut-être ajuster une contrainte.
Lorsque vous ajoutez un scrollview dans IB, il obtiendra automatiquement toutes les contraintes dont vous avez besoin. Vous ne voulez probablement pas ajouter une contrainte par programme (du moins pas sans supprimer la contrainte inférieure existante).
Deux approches pourraient être plus simples:
Créez un IBOutlet
pour votre contrainte de fond existante, par exemple scrollViewBottomConstraint
. Ensuite, vous pouvez simplement faire
self.scrollViewBottomConstraint.constant = 0.0;
Ou créez votre vue initialement dans IB où la contrainte inférieure est 0.0 et vous n'avez alors rien à faire du tout par programmation. Si vous souhaitez mettre en page une longue vue de défilement et ses sous-vues, sélectionnez le contrôleur, définissez ses métriques simulées de "déduit" à "forme libre". Ensuite, vous pouvez modifier la taille de la vue, définir les contraintes supérieure et inférieure de la vue de défilement sur zéro, mettre en page tout ce que vous voulez à l'intérieur de la vue de défilement, puis lorsque la vue est présentée au moment de l'exécution, la vue sera redimensionnée de manière appropriée et parce que vous 'ai défini les contraintes supérieure et inférieure de scrollview à 0,0, il sera redimensionné correctement. Cela semble un peu étrange dans IB, mais cela fonctionne comme un charme lorsque l'application fonctionne.
Si vous êtes déterminé à ajouter une nouvelle contrainte, vous pouvez soit supprimer par programme l'ancienne contrainte inférieure, soit définir la priorité des anciennes contraintes inférieures aussi bas que possible, et ainsi votre nouvelle contrainte (avec une priorité plus élevée) aura priorité, et l'ancienne contrainte inférieure de faible priorité ne sera gracieusement pas appliquée.
Mais vous ne voulez certainement pas simplement ajouter une nouvelle contrainte.
Il est possible de créer des prises pour représenter les contraintes de disposition dans votre contrôleur de vue. Sélectionnez simplement la contrainte que vous souhaitez dans le générateur d'interface (par exemple via "sélectionner et modifier" dans le volet des mesures de la vue que vous organisez). Accédez ensuite au volet des prises et faites glisser une "Nouvelle prise de référence" vers votre fichier de code (.h ou .m). Cela liera la contrainte à une instance NSLayoutConstraint
à laquelle vous pouvez accéder à partir de votre contrôleur et l'ajuster dynamiquement à la volée (généralement via la propriété constant
, qui est mal nommée car ce n'est pas du tout une constante ).
(Notez que dans XCode 6, vous pouvez double-cliquer sur la contrainte pour la sélectionner pour l'édition.)
Soyez prudent lorsque vous ajustez la disposition dans le générateur d'interface, car vous risquez de supprimer la contrainte et de la lier à nouveau à la prise.
En regardant les informations de la console, j'ai l'impression que vous créez une ambiguïté lorsque vous ajoutez deux mêmes types de contrainte.
Donc, au lieu de créer et d'ajouter une nouvelle contrainte, essayez de mettre à jour la contrainte précédente qui se trouve déjà dans le tableau des contraintes.
for(NSLayoutConstraint *constraint in self.view.constraints)
{
if(constraint.firstAttribute == NSLayoutAttributeBottom && constraint.secondAttribute == NSLayoutAttributeBottom &&
constraint.firstItem == self.view && constraint.secondItem == self.scrollView)
{
constraint.constant = 0.0;
}
}
J'espère que cela t'aides
Même la réponse de Rob fonctionnera!
Vous pouvez utiliser https://github.com/SnapKit/Masonry pour ajouter des contraintes par programme.
C'est la puissance d'AutoLayout NSLayoutConstraints avec une syntaxe simplifiée, chaînable et expressive. Prend en charge la mise en page automatique iOS et OSX.
UIView *superview = self.view;
UIView *view1 = [[UIView alloc] init];
view1.translatesAutoresizingMaskIntoConstraints = NO;
view1.backgroundColor = [UIColor greenColor];
[superview addSubview:view1];
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[superview addConstraints:@[
//view1 constraints
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeTop
multiplier:1.0
constant:padding.top],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:padding.left],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-padding.bottom],
[NSLayoutConstraint constraintWithItem:view1
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:superview
attribute:NSLayoutAttributeRight
multiplier:1
constant:-padding.right],]];
en quelques lignes
Voici les mêmes contraintes créées à l'aide de MASConstraintMaker
UIEdgeInsets padding = UIEdgeInsetsMake(10, 10, 10, 10);
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.equalTo(superview.mas_top).with.offset(padding.top); //with is an optional semantic filler
make.left.equalTo(superview.mas_left).with.offset(padding.left);
make.bottom.equalTo(superview.mas_bottom).with.offset(-padding.bottom);
make.right.equalTo(superview.mas_right).with.offset(-padding.right);
}];
Ou encore plus court
[view1 mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(superview).with.insets(padding);
}];
faire de votre mieux, c'est trier;)