Supposons que j'ai ajouté plus de vues dans UIStackView qui peuvent être affichées, comment puis-je faire défiler le UIStackView
?
Au cas où quelqu'un chercherait une solution sans code, j'ai créé un exemple pour le faire complètement dans le scénarimage, à l'aide de la disposition automatique.
Vous pouvez l'obtenir de github .
Fondamentalement, pour recréer l'exemple:
UIScrollView
et définissez ses contraintes.UIStackView
au UIScrollView
Leading
, Trailing
, Top
& Bottom
doivent être égales à celles de UIScrollView
Width
égale entre UIStackView
et UIScrollView
. UIStackView
.UIViews
à UIStackView
Le Guide de mise en page automatique d'Apple comprend une section entière sur Utilisation des vues de défilement . Quelques extraits pertinents:
- Épinglez les bords supérieur, inférieur, avant et arrière de la vue de contenu sur les bords correspondants de la vue de défilement. La vue du contenu définit maintenant la zone de contenu de la vue de défilement.
- (Facultatif) Pour désactiver le défilement horizontal, définissez la largeur de la vue de contenu égale à celle de la vue de défilement. La vue du contenu remplit maintenant le faire défiler la vue horizontalement.
- (Facultatif) Pour désactiver le défilement vertical, définissez la hauteur de la vue Contenu sur la hauteur de la vue Défilement. La vue du contenu se remplit maintenant la vue de défilement horizontalement.
En outre:
Votre mise en page doit définir complètement la taille de la vue de contenu (sauf le Défini aux étapes 5 et 6). … Lorsque la vue du contenu est plus haute que la vue par défilement, cette dernière permet le défilement vertical. Lorsque la vue Contenu est plus large que la vue Défilement, la vue Défilement permet le défilement horizontal.
Pour résumer, la vue du contenu de la vue de défilement (dans ce cas, une vue de la pile) doit être épinglée sur ses bords et et sa largeur et/ou sa hauteur sont sinon contraintes. Cela signifie que le contenu de la vue de pile doit être contraint (directement ou indirectement) dans la ou les directions dans lesquelles le défilement est souhaité, ce qui peut impliquer l'ajout d'une contrainte de hauteur à chaque vue dans une vue de pile défilant verticalement, par exemple. Voici un exemple de la procédure permettant le défilement vertical d'une vue de défilement contenant une vue de pile:
// Pin the edges of the stack view to the edges of the scroll view that contains it
stackView.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
stackView.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
stackView.trailingAnchor.constraintEqualToAnchor(scrollView.trailingAnchor).active = true
stackView.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
// Set the width of the stack view to the width of the scroll view for vertical scrolling
stackView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
Comme le dit Eik, UIStackView et UIScrollView jouent bien ensemble, voir ici .
La clé est que UIStackView gère la hauteur/largeur variable pour différents contenus et qu'UIScrollView fait ensuite son travail de défilement/rebondissement de ce contenu:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scrollView.contentSize = CGSize(width: stackView.frame.width, height: stackView.frame.height)
}
Les contraintes de la réponse la plus votée ici ont fonctionné pour moi et j'ai collé une image des contraintes ci-dessous, telles qu'elles ont été créées dans mon story-board.
J'ai cependant rencontré deux problèmes que les autres devraient connaître:
Après avoir ajouté des contraintes similaires à celles de la réponse acceptée, j'obtiendrais le code d'erreur rouge automatique Need constraints for: X position or width
. Cela a été résolu en ajoutant un UILabel en tant que sous-vue de la vue de pile.
J'ajoute les sous-vues par programme, de sorte que je n'avais initialement aucune sous-vue sur le storyboard. Pour vous débarrasser des erreurs d'autolayout, ajoutez une sous-vue au storyboard, puis supprimez-la au chargement avant d'ajouter vos vraies sous-vues et contraintes.
À l’origine, j’ai tenté d’ajouter UIButtons
à UIStackView. Les boutons et les vues se chargeraient, mais la vue défilement ne défilerait pas . Ce problème a été résolu en ajoutant UILabels
à la vue de pile au lieu de boutons. En utilisant les mêmes contraintes, cette hiérarchie de vues avec les étiquettes UIL défile, mais pas les boutons UIB.
Je suis déconcerté par ce problème, car les boutons UIButtons do semblent avoir un IntrinsicContentSize (utilisé par la vue Pile). Si quelqu'un sait pourquoi les boutons ne fonctionnent pas, j'aimerais savoir pourquoi.
Voici ma hiérarchie de vues et mes contraintes, à titre de référence:
Je cherchais à faire la même chose et suis tombé sur cet excellent post. } Si vous voulez le faire par programme à l'aide de l'API d'ancrage, c'est la voie à suivre.
Pour résumer, intégrez votre UIStackView
dans votre UIScrollView
et définissez les contraintes d'ancrage de UIStackView
pour qu'elles correspondent à celles de UIScrollView
:
stackView.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
stackView.trailingAnchor.constraintEqualToAnchor(scrollView.trailingAnchor).active = true
stackView.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
stackView.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
stackView.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
Ajoutez simplement ceci à viewdidload
:
let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets
Avoir une scène vide en plein écran
Ajouter une vue de défilement. Faites glisser tout en appuyant sur la touche de défilement de la vue défilante vers la vue de base, ajoutez-la de gauche à droite, de haut en bas, à zéro.
Ajouter une vue de pile dans la vue de défilement. Faites glisser le contrôle de la vue de la pile vers la vue de défilement, ajoutez-le de gauche à droite, de haut en bas, à zéro.
Placez un UILabel dans la vue de pile.
Pour plus de clarté, rendez la couleur de fond rouge; définissez la hauteur sur 100 et, dans la vue de votre pile, définissez l’espacement sur 20.
Maintenant définissez la largeur de UILabel:
Étonnamment, faites glisser la UILabel
vers la vue de défilement tout en maintenant la touche Contrôle enfoncée, pas la vue de pile, puis sélectionnez des largeurs égales.
Répéter:
C'est si simple. C'est le secret.
Vous avez terminé. C'est si simple.
Conseil: essayez d’ajouter un nouvel élément à la vue Pile, par exemple, un UIView (peut-être utilisé comme espace). Notez que tout va mal. En fait, vous (vous devez ajouter une hauteur à chaque nouvel élément} (disons "100"). Chaque fois que vous ajoutez un nouvel élément, vous devez lui donner une hauteur.
Une autre façon de voir les choses:
Dans ce qui précède, il est dit ceci: étonnamment, définissez les largeurs des étiquettes UIL sur la largeur de la vue de défilement (pas la vue de la pile). Cela fonctionne parfaitement.
Alternativement...
Notez que la vue de pile a été épinglée à gauche et à droite de son parent, la vue de défilement. Essaye ça:
Donc, vous avez deux options:
ou
Pour être clair, FAITES UNE de ces méthodes, ne faites PAS les deux.
Vous pouvez essayer ScrollableStackView: https://github.com/gurhub/ScrollableStackView
C'est une bibliothèque compatible Objective-C et Swift. Il est disponible via CocoaPods .
Exemple de code (Swift)
import ScrollableStackView
var scrollable = ScrollableStackView(frame: view.frame)
view.addSubview(scrollable)
// add your views with
let rectangle = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 55))
rectangle.backgroundColor = UIColor.blue
scrollable.stackView.addArrangedSubview(rectangle)
// ...
Exemple de code (Objective-C)
@import ScrollableStackView
ScrollableStackView *scrollable = [[ScrollableStackView alloc] initWithFrame:self.view.frame];
scrollable.stackView.distribution = UIStackViewDistributionFillProportionally;
scrollable.stackView.alignment = UIStackViewAlignmentCenter;
scrollable.stackView.axis = UILayoutConstraintAxisVertical;
[self.view addSubview:scrollable];
UIView *rectangle = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 55)];
[rectangle setBackgroundColor:[UIColor blueColor]];
// add your views with
[scrollable.stackView addArrangedSubview:rectangle];
// ...
Pour une vue empilée ou simple, la vue défilement doit avoir une largeur fixe avec la vue racine. La vue de pile principale située à l'intérieur de la vue de défilement doit avoir la même largeur. [Ma vue de défilement est en dessous d'une vue, ignorez-la]
Configurez une contrainte de largeur égale entre UIStackView et UIScrollView.
Si vous êtes contraint de centrer la vue de pile verticalement dans la vue de défilement, supprimez-la.
Placez une vue de défilement sur votre scène et redimensionnez-la pour qu'elle remplisse la scène. Ensuite, placez une vue de pile dans la vue de défilement et placez le bouton Ajouter un élément à l'intérieur de la vue de pile. Dès que tout est en place, définissez les contraintes suivantes:
Scroll View.Leading = Superview.LeadingMargin
Scroll View.Trailing = Superview.TrailingMargin
Scroll View.Top = Superview.TopMargin
Bottom Layout Guide.Top = Scroll View.Bottom + 20.0
Stack View.Leading = Scroll View.Leading
Stack View.Trailing = Scroll View.Trailing
Stack View.Top = Scroll View.Top
Stack View.Bottom = Scroll View.Bottom
Stack View.Width = Scroll View.Width
code: Stack View.Width = Scroll View.Width
est la clé.
Exemple pour un stackview/scrollview vertical (utilisant la variable EasyPeasy
pour autolayout):
let scrollView = UIScrollView()
self.view.addSubview(scrollView)
scrollView <- [
Edges(),
Width().like(self.view)
]
let stackView = UIStackView(arrangedSubviews: yourSubviews)
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
scrollView.addSubview(stackView)
stackView <- [
Edges(),
Width().like(self.view)
]
Assurez-vous simplement que chaque hauteur de votre sous-vue est définie!
Pour le défilement horizontal. Tout d’abord, créez une UIStackView
et une UIScrollView
et ajoutez-les à votre vue de la manière suivante:
let scrollView = UIScrollView()
let stackView = UIStackView()
scrollView.addSubview(stackView)
view.addSubview(scrollView)
N'oubliez pas de définir translatesAutoresizingMaskIntoConstraints
sur false
sur UIStackView
et sur UIScrollView
:
stackView.translatesAutoresizingMaskIntoConstraints = false
scrollView.translatesAutoresizingMaskIntoConstraints = false
Pour que tout fonctionne correctement, les ancres de fin, supérieure et inférieure de UIStackView
doivent être égales aux ancres UIScrollView
:
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
Mais la largeur de l'ancre de la UIStackView
doit être égale ou supérieure à la largeur de la UIScrollView
ancre:
stackView.widthAnchor.constraint(greaterThanOrEqualTo: scrollView.widthAnchor).isActive = true
Maintenant, ancrez votre UIScrollView
, par exemple:
scrollView.heightAnchor.constraint(equalToConstant: 80).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo:view.safeAreaLayoutGuide.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo:view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo:view.trailingAnchor).isActive = true
Ensuite, je suggérerais d'essayer les paramètres suivants pour l'alignement et la distribution UIStackView
:
topicStackView.axis = .horizontal
topicStackView.distribution = .equalCentering
topicStackView.alignment = .center
topicStackView.spacing = 10
Enfin, vous devrez utiliser la méthode addArrangedSubview:
pour ajouter des sous-vues à votre UIStackView.
Une autre fonctionnalité qui pourrait vous être utile est que, puisque la UIStackView
est conservée dans une UIScrollView
, vous avez maintenant accès à des encarts de texte pour rendre les choses plus belles.
let inset:CGFloat = 20
scrollView.contentInset.left = inset
scrollView.contentInset.right = inset
// remember if you're using insets then reduce the width of your stack view to match
stackView.widthAnchor.constraint(greaterThanOrEqualTo: topicScrollView.widthAnchor, constant: -inset*2).isActive = true
D'abord et avant tout, concevez votre vue, de préférence dans Sketch ou obtenez une idée précise de ce que vous voulez comme contenu défilable.
Ensuite, définissez la forme libre du contrôleur de vue (choisissez l’attribut Inspecteur) et réglez la hauteur et la largeur en fonction du contenu intrinsèque taille de votre vue (à choisir dans l'inspecteur de taille).
Après cela, dans le contrôleur de vue, placez une vue de défilement et c’est une logique Que j’ai trouvée fonctionner presque tout le temps sous iOS (il peut être nécessaire de consulter la documentation de cette classe de vue que l’on peut obtenir via la commande + cliquez sur cette classe ou via googling)
Si vous travaillez avec plusieurs vues, commencez par une vue qui a été introduite plus tôt ou est plus primitive, puis passez à la vue qui a été introduite plus tard ou plus moderne. Donc, ici, puisque la vue par défilement a été introduite en premier, commencez d'abord par la vue par défilement, puis passez à la vue par pile. Ici, mettez les contraintes de vue de défilement à zéro dans toutes les directions vis-à-vis de sa super vue. Placez toutes vos vues dans cette vue par défilement, puis placez-les dans la vue pile.
Tout en travaillant avec la vue de pile
Commencez d'abord par les motifs (approche ascendante), c’est-à-dire si vous avez des étiquettes, des champs de texte et des images dans votre vue, puis disposez ces vues d’abord (dans la vue de défilement) et ensuite, placez-les dans la vue pile.
Après cela Tweak la propriété de la vue de la pile. Si la vue souhaitée n'est toujours pas atteinte, utilisez une autre vue de pile.
Après avoir créé votre vue, définissez une contrainte entre la vue de la pile mère et la vue du défilement, tout en contraignant les enfants à afficher la vue de la pile mère . Espérons que cette fois-ci, tout devrait bien fonctionner ou vous pourrez recevoir un avertissement de Xcode. , lisez ce qu’il dit et appliquez-les. Espérons que maintenant vous devriez avoir une vue opérationnelle selon vos attentes :).
Si quelqu'un cherche horizontalement scrollview
func createHorizontalStackViewsWithScroll() {
self.view.addSubview(stackScrollView)
stackScrollView.translatesAutoresizingMaskIntoConstraints = false
stackScrollView.heightAnchor.constraint(equalToConstant: 85).isActive = true
stackScrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
stackScrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
stackScrollView.bottomAnchor.constraint(equalTo: visualEffectViews.topAnchor).isActive = true
stackScrollView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: stackScrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: stackScrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: stackScrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: stackScrollView.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: stackScrollView.heightAnchor).isActive = true
stackView.distribution = .equalSpacing
stackView.spacing = 5
stackView.axis = .horizontal
stackView.alignment = .fill
for i in 0 ..< images.count {
let photoView = UIButton.init(frame: CGRect(x: 0, y: 0, width: 85, height: 85))
// set button image
photoView.translatesAutoresizingMaskIntoConstraints = false
photoView.heightAnchor.constraint(equalToConstant: photoView.frame.height).isActive = true
photoView.widthAnchor.constraint(equalToConstant: photoView.frame.width).isActive = true
stackView.addArrangedSubview(photoView)
}
stackView.setNeedsLayout()
}