web-dev-qa-db-fra.com

Est-il possible pour UIStackView de faire défiler?

Supposons que j'ai ajouté plus de vues dans UIStackView qui peuvent être affichées, comment puis-je faire défiler le UIStackView?

147
itsaboutcode

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:

  1. Créez un UIScrollView et définissez ses contraintes.
  2. Ajoutez un UIStackView au UIScrollView
  3. Définissez les contraintes: Leading, Trailing, Top & Bottom doivent être égales à celles de UIScrollView
  4. Définissez une contrainte Width égale entre UIStackView et UIScrollView
  5. Définissez Axis = Vertical, Alignment = Fill, Distribution = Espacement égal et Espacement = 0 sur UIStackView.
  6. Ajoutez un nombre de UIViews à UIStackView
  7. Courir
436
Arjan

Le Guide de mise en page automatique d'Apple comprend une section entière sur Utilisation des vues de défilement . Quelques extraits pertinents:

  1. É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.
  2. (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.
  3. (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
33
titaniumdecoy

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)       
}
12
user1307434

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: 

  1. 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.

  2. À 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:

constraints for stack view in scroll view[1]

11
pkamb

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
8
Dalmazio

Ajoutez simplement ceci à viewdidload:

let insets = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
scrollVIew.contentInset = insets
scrollVIew.scrollIndicatorInsets = insets

source: https://developer.Apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/LayoutUsingStackViews.html

5
rashad.z

Voici l'explication la plus simple possible:

  1. Avoir une scène vide en plein écran

  2. 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.

  3. 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.

  4. 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.

  1. 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:

Ne contrôlez pas le glisser de UILabel vers le parent de UILabel - allez chez le grand-parent. (En d’autres termes, allez jusqu’à la vue de défilement, n’allez pas à la vue de la pile.)

C'est si simple. C'est le secret.

  1. Ensuite, vous devez cliquez sur l’unique UILabel et vous devez cliquez littéralement à plusieurs reprises sur le copier-coller. Il ne fonctionnera pas avec un seul élément. (Ne cliquez PAS sur "dupliquer", cliquez sur "copier puis coller".)

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:

Faites glisser de la vue de pile vers la vue de défilement et ajoutez une contrainte "largeur égale". Cela semble étrange car vous avez déjà épinglé gauche-droite, mais c'est comme cela que vous le faites. Aussi étrange que cela puisse paraître, c'est le secret.

Donc, vous avez deux options:

  1. Étonnamment, définissez la largeur des étiquettes UIL sur la largeur du grand-parent scrollview (pas le parent stackview).

ou

  1. Étonnamment, définissez une "largeur égale" de la vue pile dans la vue défilement - même si vous avez quand même les bords gauche et droit de la vue pile épinglés à la vue défilement.

Pour être clair, FAITES UNE de ces méthodes, ne faites PAS les deux.

2
Fattie

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]; 
// ...
2
mgyky

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.

 enter image description here

0
Md Imran Choudhury

Si vous êtes contraint de centrer la vue de pile verticalement dans la vue de défilement, supprimez-la.

0
Ahmed Elashker

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

enter image description here

code: Stack View.Width = Scroll View.Width est la clé.

0
Joshua Cleetus

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!

0
dOM

Défilement horizontal (UIStackView dans UIScrollView)

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.

Encarts de texte

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
0
sketchyTech
  1. 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.

  2. 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).

  3. 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. 

  • Si ce n'est toujours pas le cas, jouez avec la résistance à la compression ou la priorité au contenu.
  • Après cela, ajoutez des contraintes à la vue de la pile.
  • Pensez également à utiliser un UIView vide comme vue de remplissage, si tout ce qui précède ne donne pas de résultats satisfaisants.

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 :).

0
Nikhil Pandey

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()

 }
0
user1039695