J'ai passé deux jours à essayer différentes solutions pour approches Mixte et Pure Autolayout pour réaliser ce qui était une configuration de scrollview triviale avant l'autolayout, et c'est maintenant officiel - je dois être trop stupide. Je l’installe principalement dans Storyboard (enfin, c’est comme ça).
Alors, voici mon appel à l'aide.
Viewtree:
UIView
-UIView
-UIView
..-UIScrollview
...-UIButton
...-UIButton
...-UIButton
Les boutons sont supposés défiler horizontalement (de gauche à droite et inversement). Quelqu'un peut-il s'il vous plaît me faire savoir comment définir les contraintes pour y parvenir en utilisant Autolayout pur ???
-
J'ai essayé l'approche mixte, comme ceci:
UIView
- UIView
- UIView
..-UIScrollview
...-UIView (contentview)
....-UIButton
....-UIButton
....-UIButton
... et en définissant des contraintes de largeur et de hauteur fixes pour les paramètres contentview
et translatesAutoresizingMaskIntoConstraints
conformément à la note technique d'Apple. Les boutons et la vue de défilement sont configurés à l'aide de contraintes. Cela fait défiler la vue de défilement (yay) mais hélas, ça défile trop! Autant que je sache, la largeur de défilement est en quelque sorte doublée par rapport à ce que je règle la contentview à ??? !!! ???
J'ai aussi essayé la méthode de l'autolayout pur, à la fois avec contentview
et sans. Toutes les vues sont translatesAutoresizingMaskIntoConstraints=NO
, sauf pour self.view
. Les boutons ont des contraintes de largeur/hauteur fixes et sont épinglés aux quatre bords de la vue à défilement. Rien ne défile.
Je ne comprends donc pas pourquoi je ne parviens pas à le faire fonctionner correctement. Toute aide est très appréciée, et si vous avez besoin d’autres informations, n'hésitez pas à demander!
MISE À JOUR Copie d'écran avec la solution - contraintes de buttonZ:
EDIT @ Jamie Forrest La solution s'avère donc être une contrainte de fin erronée pour le dernier bouton. Au lieu de 6441, la valeur que j'avais définie était négative, -6441. Le problème, c’est que lors de la définition de la valeur dans le storyboard, la barre d’outils Épingler offre deux options:
La valeur actuelle du canevas est négative (il n’ya pas de défilement) et l’option ci-dessous est positive (activation du défilement). Cela signifie que je ne suis pas stupide mais au moins à moitié aveugle je suppose. Bien que, pour ma défense, ne soit-il pas quelque peu gênant que XCode n'indique pas d'erreur pour le réglage "incorrect"?
EDITE ENCORE Maintenant, c'est amusant ... changer la valeur de fin de -6441 (pas de défilement) à 6441 activé. Mais mon vieil ami, le "trop grand contenu" était de retour, ce qui a conduit à une taille de contenu deux fois plus grande que ce qu'elle devrait être! La solution pour obtenir le défilement correct du contenu consistait à définir la contrainte de fin sur ZERO! Cela n’est pas évident lorsque vous travaillez dans Storyboard, mais que vous regardez le code de @Infinity James, c’est ce qu’il devrait être.
Il est difficile de voir les valeurs exactes et la configuration de vos contraintes lorsque vous les avez collées ici. Je ne suis donc pas sûr de regarder sur vos captures d'écran où vous vous êtes trompé.
Au lieu d'expliquer ce qui ne va pas dans votre configuration, j'ai créé un exemple de projet avec une hiérarchie de vues et une configuration de contraintes très similaires à celles que vous décrivez. Le défilement horizontal fonctionne comme prévu dans l'exemple de projet, qui utilise l'approche "Pure AutoLayout" décrite par Apple dans la note technique .
J'ai également eu beaucoup de mal à faire fonctionner Auto Layout avec UIScrollView
à l'origine. La clé pour le faire fonctionner est de s’assurer que tous les éléments de la vue défilement, pris ensemble, ont des contraintes qui lient finalement à tous les côtés de la vue défilement et qui permettent au système AutoLayout de déterminer un contentSize pour le faire défiler la vue qui sera plus grande que son cadre. Il semble que vous essayiez de faire cela dans votre code, mais vous aviez peut-être des contraintes superflues qui rendaient la taille de contentSize trop petite.
Il convient également de noter que, comme d'autres l'ont mentionné, avec AutoLayout et UIScrollview, vous ne définissez plus explicitement contentSize. Le système AutoLayout calcule le contentSize en fonction de vos contraintes.
J'ai aussi trouvé ce chapitre de l'ebook très utile pour me faire comprendre comment tout cela fonctionne. J'espère que tout ça aide.
LOL bienvenue au club de la stupidité. Je suis l'un des fondateurs. :RÉ
Pour le défilement VERTICAL : le seul moyen de le faire fonctionner (iOS 8, Xcode 6 et autolayout pur) consistait à ajouter les contraintes suivantes à ma vue Défilement. (tous liés à la superview):
Ma structure:
UIView
- ScrollView
- Subview
- Subview
- Subview
- Subview
- ...
C'est le résultat final:
C'est la configuration:
Espérons que cela éviterait à quelqu'un d'aller à dormir AT 5 heures du matin. :RÉ
À en juger par le nombre élevé de votes sur la question et le faible nombre de votes sur les réponses, les gens ne trouvent pas de solution compréhensible et rapide ici. Laissez-moi essayer d'en ajouter un. Ce projet est un exemple autonome entièrement réalisé dans Interface Builder. Vous devriez être capable de le faire en 10 minutes ou moins. Ensuite, vous pouvez appliquer les concepts que vous avez appris à votre propre projet.
La question initiale concerne les boutons de défilement. Ici, je viens d'utiliser UIView
s mais ils peuvent représenter la vue de votre choix. J'ai également choisi le défilement horizontal car les captures d'écran du storyboard sont plus compactes pour ce format. Les principes sont les mêmes pour le défilement vertical, cependant.
UIScrollView
ne devrait utiliser qu'une seule sous-vue. Il s'agit d'un 'UIView' qui sert d'affichage de contenu pour tout ce que vous souhaitez faire défiler.Il peut s'agir simplement d'une application à vue unique.
Dans cet exemple, nous allons faire une vue de défilement horizontal. Sélectionnez le contrôleur de vue, puis choisissez forme libre dans l'inspecteur de taille. Faites la largeur 1,000
et la hauteur 300
. Cela nous donne juste la place sur le storyboard pour ajouter du contenu qui défilera.
Ajouter une vue de défilement
Ajoutez un UIScrollView
et épinglez les quatre côtés à la vue racine du contrôleur de vue.
Ajouter une vue de contenu
Ajoutez une UIView
en tant que sous-vue à la vue de défilement. Ceci est la clé. N'essayez pas d'ajouter beaucoup de sous-vues à la vue de défilement. Il suffit d'ajouter un seul UIView
. Ce sera votre vue de contenu pour les autres vues que vous voulez faire défiler. Épinglez la vue du contenu à la vue de défilement sur les quatre côtés.
Égalité de hauteur
Maintenant dans la structure du document, Command Cliquez à la fois sur la vue Contenu et sur la vue défilante vue parent pour les sélectionner tous les deux. Puis définissez les hauteurs pour être égal (Control glisser de la vue du contenu vers la vue du défilement). Ceci est également essentiel. Étant donné que nous faisons défiler horizontalement, la vue du contenu de la vue de défilement ne saura pas à quelle hauteur elle devrait être si nous ne la définissons pas de cette façon.
Remarque:
Ajouter du contenu
Ajoutez trois UIView
s et donnez-leur toutes les contraintes. J'ai utilisé 8 marges de points pour tout.
Contraintes:
Le réglage des contraintes de largeur est également essentiel afin que la vue de défilement sache à quel point sa vue de contenu sera étendue.
C'est tout. Vous pouvez exécuter votre projet maintenant. Il devrait se comporter comme l'image défilante en haut de cette réponse.
Pour le défilement vertical, intervertissez simplement les directions de largeur et de hauteur dans cet exemple (testé et fonctionnel).
ContentSize est défini implicitement en appliquant les contraintes à l'intérieur de UIScrollView.
Par exemple, si vous avez un UIScrollView à l'intérieur d'un UIView, il ressemblera à ceci (comme vous le savez sûrement):
UIView *containerView = [[UIView alloc] init];
UIScrollView *scrollView = [[UIScrollView alloc] init];
[containerView addSubview:scrollView];
containerView.translatesAutoresizingMaskIntoConstraints = NO;
scrollView.translatesAutoresizingMaskIntoConstraints = NO;
NSDictionary *viewsDictionary = NSDictionaryOfVariableBindings(containerView, scrollView);
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[scrollView]|"
options:kNilOptions
metrics:nil
views:viewsDictionary]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|"
options:kNilOptions
metrics:nil
Cela définira le scrollView pour qu'il remplisse la taille du conteneurView (le conteneurView devra donc avoir une certaine taille).
Vous pouvez ensuite ajuster le contentSize de UIScrollView en le définissant implicitement pour qu'il soit assez grand pour contenir les boutons comme ceci:
UIButton *buttonA = [[UIButton alloc] init];
UIButton *buttonB = [[UIButton alloc] init];
UIButton *buttonC = [[UIButton alloc] init];
[scrollView addSubview:buttonA];
[scrollView addSubview:buttonB];
[scrollView addSubview:buttonC];
buttonA.translatesAutoresizingMaskIntoConstraints = NO;
buttonB.translatesAutoresizingMaskIntoConstraints = NO;
buttonC.translatesAutoresizingMaskIntoConstraints = NO;
viewsDictionary = NSDictionaryOfVariableBindings(scrollView, buttonA, buttonB, buttonC);
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[buttonA]-|"
options:kNilOptions
metrics:nil
views:viewsDictionary]];
[scrollView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-[buttonA]-[buttonB]-[buttonC]-|"
options:NSLayoutFormatAlignAllBaseline
metrics:nil
views:viewsDictionary]];
Il y a tellement de questions sur l'utilisation de AutoLayout avec UIScrollView, le point clé que nous ignorons est que les vues internes de UIScrollView imposent des contraintes à la vue Contenu mais pas le UIScrollView lui-même. Reportez-vous à la Note technique TN2154 , vous trouverez:
La classe UIScrollView fait défiler son contenu en modifiant l'origine de ses limites. Pour que cela fonctionne avec la disposition automatique, les bords supérieur, gauche, droit et à l'intérieur d'une vue de défilement désignent maintenant les bords de sa vue de contenu.
La figure suivante montre que:
Vous pouvez trouver que l'espace de fin est de 500 points. Si la contrainte est appliquée à UIScrollView, la vue sera manquante et devra être mise à jour de son cadre. Cependant, aucun avertissement et aucune erreur. Parce que toutes les contraintes sont contre la vue du contenu.
UIScrollView calculera la taille de la vue de contenu en fonction des contraintes des vues internes. (Pour l'exemple, la taille du contenu: width = 100 (espace de début) + 200 (largeur de la vue) + 500 (espace de fin), height = 131 (espacement supérieur) + 200 (hauteur) + 269 (espacement inférieur)
Comment ajouter des contraintes pour les vues dans UIScrollView:
Et tout ça c'est fait.
Un moyen facile de gérer la mise en forme automatique avec scrollview consiste à ajouter une vue de conteneur contenant toutes les sous-vues dans la vue de défilement.
Conclusion: le point clé pour comprendre la mise en forme automatique avec UIScrollView est que les vues internes imposent des contraintes à la vue de contenu, mais pas à UIScrollView lui-même.
attaché exemple de code
La solution suivante a fonctionné pour moi pour scrollView avec autolayout et sans contentSize:
Et tu as fini. Maintenant, vous pouvez ajouter un nombre quelconque de contrôles sur cette vue et appliquer les contraintes pertinentes les unes aux autres (qui ne semblent pas fonctionner sans cette vue). Si vous ne souhaitez pas utiliser cette vue, vous devrez appliquer des contraintes pour chaque contrôle lié à scrollView (non liées entre elles).
Critique. Disons que, pour plus de clarté, UIScrollView est largeur de 10 et 100 de hauteur. (En fait, normalement, ces valeurs sont dynamiques, bien sûr, en fonction de la largeur de l'appareil, etc. Mais pour l'instant, disons simplement 1000 larges et 100 hauts.) Disons que vous faites un défilement horizontal. Mettez donc UIView dans UIScrollView. (C’est la "vue du contenu".) Définissez les quatre contraintes de la vue du contenu en haut, en bas, en tête, en fin, sur la vue de défilement. Mettez-les tous à zéro même si cela semble faux. Réglez la hauteur du contenu UIView sur 100 et oubliez cela. Maintenant: vous voulez faire défiler horizontalement, définissez donc la largeur de la vue du contenu sur 1225.
Notez que la largeur de la vue de contenu est maintenant 225 fois supérieure à la largeur de la vue de défilement parent. Ce n'est pas grave: en fait, vous DEVEZ le faire. Notez que
vous pensez que vous devez "faire correspondre" les largeurs comme vous le feriez normalement. Mais si vous faites cela, cela ne fonctionnera pas du tout.
Il est intéressant de noter que vous pouvez réellement définir les nombres de début/fin sur n’importe quelle valeur positive (essayez de dire "50") et cela vous donne une sorte de marge de rebond. (Cela a souvent l’air génial: essayez-le.) Toute valeur négative à l’une ou l’autre des extrémités sera "brisée en silence".
Notez que, souvent, très Xcode (à partir de la version 7.3.1),
car il essaie de les compter automatiquement pour vous. Si c'est le cas, il va se casser silencieusement. Définissez les quatre valeurs à zéro dans la première instance. Et définissez la largeur de la vue du contenu beaucoup plus large que le "1000" dans l'exemple.
Edited: J'ai fini par utiliser UITableView au lieu de UIScrollView pour la plupart de mes besoins. Comme tableView me semble beaucoup plus flexible et dynamique.
Je suppose que vous rencontrez des problèmes avec thecontentSize
. Découvrez this blog post sur la façon de gérer la contentSize
lorsqu’on utilise une approche "pure" AutoLayout. En résumé, vos contraintes définissent implicitement la taille du contenu. Vous ne le définissez JAMAIS explicitement lorsque vous utilisez AutoLayout. J'ai joint un exemple de projet à la fin de l'article pour en montrer le fonctionnement
Vous devriez organiser votre mise en page comme ceciViewControllerView
contient ScrollView
, ScrollView
contient ContainerView
, ContainerView
contient 2 Labels
Puis suivez les 3 étapes pour que votre ScrollView
puisse défiler
ScrollView
(haut/droite/bas/gauche) sur ViewControllerView
ContainerView
(haut/droite/bas/gauche) sur ScrollView
Horizontally in Container
(( n'est pas définissez Vertically in Container
)Label1
pin ( haut /droite/gauche) sur ContainerView
Label1
pin (droite/gauche/ bas ) à ContainerView
et haut à Label1
J'espère que cette aide
Vous avez peut-être examiné une partie des notes techniques. Vous pouvez définir implicitement la taille du contenu d'une vue de défilement à l'aide de contraintes fixées aux bords de la vue de défilement.
Voici un exemple simple. Créez un scénarimage avec une vue, une vue par défilement. Définissez les contraintes de défilement de la vue pour l’adapter à la taille de la vue dans laquelle vous la placez.
Dans cette vue de défilement, ajoutez une vue unique. Définissez explicitement la taille de cette vue à l'aide de contraintes (et assurez-vous qu'elle est plus grande que la vue avec défilement).
Ajoutez maintenant quatre contraintes supplémentaires à cette vue interne, ce qui verrouille les quatre bords de la vue interne à la vue de défilement parent. Ces quatre contraintes entraîneront une augmentation de la taille du contenu afin de l'adapter à la vue interne.
Si vous souhaitez ajouter plusieurs vues à une vue de défilement, par exemple horizontalement, verrouillez le côté gauche de la première sous-vue à gauche de la vue de défilement, verrouillez les sous-vues horizontalement, et la droite côté de la dernière vue secondaire à droite de la vue de défilement. Ces contraintes forceraient la taille du contenu de la vue de défilement à se développer pour prendre en compte toutes les sous-vues et leurs contraintes.
Si votre question est "Comment puis-je mettre un groupe de UITextFields dans un UIScrollView à défilement vertical, de sorte qu'ils s'éloignent du clavier lorsqu'ils ont le focus", la meilleure réponse est:
Ne pas.
Utilisez plutôt un UITableViewController avec des cellules statiques.
Vous bénéficiez gratuitement de ce comportement de défilement et de tous les encarts de contenu Just Work si votre contrôleur de vue est affiché à l'intérieur d'un UINavigationController.
Problème similaire que j'ai aujourd'hui avec iOS 8.4, Xcode 6.4
Il existe une vue contenant une vue de défilement, contenant un contentView (UIView) contenant des sous-vues.
Tout est mise en page automatique partout. Les bords de la vue de défilement sont épinglés aux bords de la vue parent avec des contraintes. Les bords de la vue de contenu sont épinglés aux bords de la vue de défilement avec des contraintes.
À l'origine, la vue du contenu refusait de dimensionner sur toute la largeur de la vue de défilement. J'ai dû ajouter une contrainte supplémentaire sur la vue de contenu pour que sa largeur corresponde à la vue de défilement parent. Ou je pourrais définir une contrainte contentView.centerX == scrollView.centerX. En plus de cerner les contours, l’un ou l’autre de ceux-ci a soudainement modifié la taille du contenu.
// Either one of these additional constraints are required to get autolayout to correctly layout the contentView. Otherwise contentView size is its minimum required size
scrollView.addConstraint(NSLayoutConstraint(item: contentView, attribute: .CenterX, relatedBy: .Equal, toItem: scrollView, attribute: .CenterX, multiplier: 1.0, constant: 0))
scrollView.addConstraint(NSLayoutConstraint(item: contentView, attribute: NSLayoutAttribute.Width, relatedBy: .Equal, toItem: scrollView, attribute: .Width, multiplier: 1.0, constant: 0.0))
Épingler les bords de la vue de contenu à la vue de défilement en utilisant des contraintes visuelles du formulaire,
let cvConstraints = ["H:|[contentView]|", "V:|[contentView]|"]
J'utilise une routine pour parcourir le tableau et les ajouter au scrollView.
J'ai fait face à un problème similaire. Je définissais chaque contrainte et je me demandais toujours pourquoi elle redimensionnait toujours certaines sous-vues. Ma solution a été de définir clipsToBounds sur YES.
L’approche autolayout pure fonctionne à merveille, mais il est très difficile d’être configuré si vous migrez de non autolayout. Je l'ai déjà fait plusieurs fois et j'ai quelques conseils d'ordre général:
Si, comme moi, vous utilisez simplement du contenu statique sans contraintes dans la sous-vue, vous pouvez le faire comme ceci:
override func viewDidLayoutSubviews() {
scrollView.contentSize = CGSizeMake(320, 800)
}
J'ai passé des jours à essayer de trouver une solution à l'utilisation de l'affichage automatique de défilement dans l'affichage automatique, afin de centrer la vue de défilement sur l'écran visible, qui fonctionne pour tous les périphériques/dimensions de l'écran, ainsi que pour la rotation de l'écran.
J'ai passé des jours à essayer de le faire avec Autolayout uniquement, et je me suis approché mais jamais assez près. Donc, au final, j'ai dû ajouter 3 lignes de code par écran, dans viewDidLoad.
Voir la solution ci-dessous:
Maintenant, allez dans le fichier .m et ajoutez ces lignes de code dans viewDidLoad (jouez avec le montant de remplissage pour obtenir le centrage vertical correct)
if (IPAD)
{self.constraintVertVtoSV.constant = 150.0; }
cela devrait maintenant fonctionner sur tous les appareils et être correctement centré et toujours bien défiler.
Au bout d’un certain temps, j’ai finalement trouvé une solution. Je travaille avec des storyboards de classes universelles (600x600). J'ai créé un UIView (contentView) de la taille du scrollView et créé des contraintes pour le haut, le bas, le début et la fin du scrollView. Ensuite, j'ai découpé manuellement la taille de contentView à 600x600. Le storyboard a cessé d'essayer de tout redimensionner et je pouvais travailler, mais la vue était horrible sur le vrai appareil ou le simulateur. J'ai fait 2 sorties de contrainte de cette taille écrêtée.
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *contentViewHeightConstraint;
Puis dans viewDidLoad
CGSize viewSize = self.view.frame.size;
self.contentViewWidthConstraint.constant = viewSize.width;
self.contentViewHeightConstraint.constant = viewSize.height;
Fonctionne très bien.
Dans Swift, vous pouvez utiliser cette solution de travail.
Contraints
ScrollView
: Début, Fin, Haut, Bas = Superview
ContentView
: Début, Fin, Haut, Bas = ScrollView. Hauteur fixe/par rapport au contenu.
Vous pouvez définir la contrainte de largeur (contentView) sur égale à scrollviews superview, mais sélectionnez remove remove au moment de la création, car vous allez ajouter cette contrainte par programmation. C'est juste pour que l'IB ne se plaint pas d'avertissements.
extension UIView {
func setupContentViewForViewWithScroll(contentView vwContent : UIView) {
//Set constraint for scrollview content
let constraint = NSLayoutConstraint(item: vwContent, attribute: NSLayoutAttribute.Width, relatedBy: .Equal, toItem: nil, attribute: NSLayoutAttribute.NotAnAttribute, multiplier: 1, constant: self.bounds.size.width)
vwContent.addConstraint(constraint)
self.layoutSubviews()
}
}
Et dans le contrôleur de vue viewDidLayoutSubviews
je viens d'appeler cette méthode:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.view.setupContentViewForViewWithScroll(contentView: vwContent)
}
Je sais que c'est une solution simple pour un profane et non pas ce que Apple suggère dans le document, mais cela a fonctionné deux fois pour moi, avec un contenu différent et peut être configuré très rapidement: Dans le contrôleur de vue de scénario, insérez UIView. Dans UIView, insérez une vue de tableau, dynamique, 0 cellules de prototype, style brut ou groupé. En mode Table, insérez une vue de défilement. En mode de défilement, insérez du contenu. C'est ça, pas de paramètres dans le contrôleur de vue personnalisé.