Étiquette multiligne dans UIStackView

154

Lorsque vous placez une étiquette multiligne (avec un saut de ligne défini sur Word Wrap) dans une vue de pile, l'étiquette perd immédiatement le saut de ligne et affiche le texte de l'étiquette sur une ligne à la place.

Pourquoi cela se produit-il et comment conserver une étiquette multiligne dans une vue de pile?

Aubaine
la source
1
Avez-vous défini le nombre de lignes sur 0?
dasdom
Avez-vous trouvé une réponse?
Brenden

Réponses:

204

La bonne réponse est ici:
https://stackoverflow.com/a/43110590/566360


  1. Incorporer l' UILabelintérieur d'un UIView(Éditeur -> Intégrer dans -> Afficher)
  2. Utilisez des contraintes pour ajuster le UILabelà UIView(par exemple, espace de fin, espace supérieur et espace de début pour superviser les contraintes)

Le UIStackViewva étirer le UIViewpour s'adapter correctement, et le UIViewcontraindra UILabelà plusieurs lignes.

Andy
la source
36
C'est tellement stupide mais ça marche! Merci mec ! Peut-être qu'un jour, Apple publiera quelque chose qui n'est pas à moitié cassé dans son SDK ...
Guillaume L.
1
Oh mec! Vous êtes un sauveur! J'y étais depuis des heures :) N'oubliez pas de définir également les lignes à 0 dans l'inspecteur d'attributs, il manquait juste cela.
absin
8
c'est une solution de contournement, la bonne solution est ici stackoverflow.com/a/43110590/566360
vietstone
2
Était suffisant pour moi juste pour changer l'alignement horizontal au centre. Aucune UIViewmanigance nécessaire.
shim
1
@Lucien c'est exactement ce qui a été dit dans cette réponse. mais la bonne réponse est ici: stackoverflow.com/a/43110590/566360
Andy
145

Pour une vue de pile horizontale qui a une UILabelcomme l'une de ses vues, dans Interface Builder d'abord défini label.numberOfLines = 0. Cela devrait permettre à l'étiquette d'avoir plus d'une ligne. Cela n'a pas fonctionné initialement pour moi lorsque la vue de la pile avait stackView.alignment = .fill. Pour le faire fonctionner, il suffit de définir stackView.alignment = .center. L'étiquette peut désormais s'étendre sur plusieurs lignes dans le UIStackView.

La documentation Apple dit

Pour tous les alignements à l'exception de l'alignement de remplissage, la vue de pile utilise la propriété intrinsèque de la taille du contenu de chaque vue arrangée lors du calcul de sa taille perpendiculaire à l'axe de la pile.

Notez le mot sauf ici. Quand .fillest utilisé, l'horizontale UIStackViewne se redimensionne PAS verticalement en utilisant les tailles de sous-vues arrangées.

pbm
la source
3
C'est la bonne solution sans hacks. La lecture de la documentation est toujours payante!
Islam Q.
3
Cela devrait être la solution acceptée. Avec la solution de contournement UIView, le texte d'UILabel flottait sur une autre sous-vue dans mon cas.
14
Cela ne fonctionne pas réellement ... J'ai 3 étiquettes dans mon stackview. J'ai mis les lignes à zéro et l'alignement au centre ... Je vois toujours que les étiquettes sont découpées.
MatterGoal
11
C'est le .alignmentde l'UIStackView que vous devez définir sur n'importe quoi, mais .fill pas sur UILabel, ce qui est un peu déroutant dans l'exemple donné label.alignment = .center. Je pense que cela devrait êtrestackView.alignment = .center
ae14
2
Mais comment puis-je faire fonctionner un UILableView à plusieurs lignes dans StackView vertical?
DàChún
42
  • Définissez d'abord le nombre de lignes d'étiquette sur 0
  • La vue de la pile ne s'agrandira toujours pas à multiLinemoins que vous ne lui donniez une largeur fixe. Lorsque nous fixons sa largeur, il passe en multiligne lorsque cette largeur est atteinte, comme indiqué:

enregistrement d'écran

Si nous ne donnons pas une largeur fixe à la vue de la pile, les choses deviennent ambiguës. Combien de temps la vue de la pile augmentera-t-elle avec l'étiquette (si la valeur de l'étiquette est dynamique)?

J'espère que cela peut résoudre votre problème.

Irfan
la source
3
Que faire si la largeur de la vue de la pile n'est pas fixe mais proportionnelle à la largeur du périphérique (c'est-à-dire de la vue de supervision).
OutOnAWeekend
Oui, nous pouvons aussi avoir une largeur proportionnelle. Le point est si l'étiquette doit se casser après une certaine largeur quelle qu'elle soit, mais doit être définie.
Irfan
@AnandKumar vous pouvez spécifier preferredMaxLayoutWidthdans viewDidLayoutSubviewspour votre étiquette dans UIViewControllerou dans layoutSubviewssi vous sous-classe UIView.
Shyngys Kassymov
4
Cela aurait dû être la réponse acceptée, tout l'intérêt du stackview est de ne pas avoir à ajouter de contraintes entre le stackview et ses éléments ...
Honey
4
Donner une valeur fixe va à l'encontre de l'objectif de la répartition automatique
Islam Q.
17

La définition de PreferredMaxLayoutWidth sur UILabel a fonctionné pour moi

self.myLabel.preferredMaxLayoutWidth = self.bounds.size.width;
Pablo Martinez
la source
3
C'est la bonne réponse dans mon livre. Chaque fois que vous jouez avec UILabel et que vous avez besoin de gérer plusieurs lignes, vous devez vraiment lui donner une préférenceMaxLayoutWidth. Cela pourrait fonctionner sans lui, mais les choses peuvent devenir plutôt funky très rapidement sans lui.
Lundi
2
Tous ont essayé toutes les réponses et toutes étaient fausses sauf la vôtre. Merci!
levan
Cela a parfaitement résolu mon problème et se sent beaucoup moins piraté, merci.
Ethan Zhao
Merci pour votre réponse! Dans mon cas, l'alignement de mon stackView était déjà défini sur .centeret je n'avais vraiment pas besoin de mon étiquette dans UIView.
Fariza Zhumat le
12

Définissez simplement le nombre de lignes sur 0 dans l'inspecteur d'attributs pour l'étiquette. Cela fonctionnera pour vous.

entrez la description de l'image ici

technerd
la source
12
Pour un stackView vertical, vous devez avoir le paramètre Distribution défini sur "Fill" pour que cela fonctionne.
SnoopyProtocol
2
Définir la distribution sur «Remplir» est la clé du correctif.
Dylan Moore du
J'ai trouvé que pour un UIStackView vertical, je devais définir des contraintes de haut et de bas pour qu'un enfant UILabel se développe et affiche tout le texte.
bruce1337
8

Après avoir essayé toutes les suggestions ci-dessus, je n'ai trouvé aucun changement de propriétés nécessaire pour UIStackView. Je change juste les propriétés des UILabels comme suit (les étiquettes sont déjà ajoutées à une vue de pile verticale):

Exemple Swift 4:

    [titleLabel, subtitleLabel].forEach(){
        $0.numberOfLines = 0
        $0.lineBreakMode = .byWordWrapping
        $0.setContentCompressionResistancePriority(UILayoutPriority.required, for: .vertical)
    }
Bill Chan
la source
5

L'astuce magique pour moi était de mettre un widthAnchorau UIStackView.

Le réglage leadingAnchoret trailingAnchorne fonctionnera pas, mais le réglage centerXAnchoret widthAnchorl'affichage de UILabel correctement.

Skoua
la source
4

iOS 9+

Appelez [textLabel sizeToFit]après avoir défini le texte de l'UILabel.

sizeToFit remettra en page l'étiquette multiligne à l'aide de la méthode favoriteMaxWidth. L'étiquette redimensionnera le stackView, ce qui redimensionnera la cellule. Aucune contrainte supplémentaire en plus d'épingler la vue de pile à la vue de contenu n'est requise.

logiciel humain
la source
4

Voici un exemple complet d'une verticale UIStackViewcomposée de multilignes UILabelavec hauteur automatique.

Les étiquettes sont enveloppées en fonction de la largeur de la vue de pile et la hauteur de la vue de la pile est basée sur la hauteur enveloppée de l'étiquette. (Avec cette approche, vous n'avez pas besoin d'incorporer les étiquettes dans a UIView.) (Swift 5, iOS 12.2)

entrez la description de l'image ici

// A vertical stackview with multiline labels and automatic height.
class ThreeLabelStackView: UIStackView {
    let label1 = UILabel()
    let label2 = UILabel()
    let label3 = UILabel()

    init() {
        super.init(frame: .zero)
        self.translatesAutoresizingMaskIntoConstraints = false
        self.axis = .vertical
        self.distribution = .fill
        self.alignment = .fill

        label1.numberOfLines = 0
        label2.numberOfLines = 0
        label3.numberOfLines = 0
        label1.lineBreakMode = .byWordWrapping
        label2.lineBreakMode = .byWordWrapping
        label3.lineBreakMode = .byWordWrapping
        self.addArrangedSubview(label1)
        self.addArrangedSubview(label2)
        self.addArrangedSubview(label3)

        // (Add some test data, a little spacing, and the background color
        // make the labels easier to see visually.)
        self.spacing = 1
        label1.backgroundColor = .orange
        label2.backgroundColor = .orange
        label3.backgroundColor = .orange
        label1.text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi."
        label2.text = "Hello darkness my old friend..."
        label3.text = "When I wrote the following pages, or rather the bulk of them, I lived alone, in the woods, a mile from any neighbor, in a house which I had built myself, on the shore of Walden Pond, in Concord, Massachusetts, and earned my living by the labor of my hands only."
    }

    required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }
}

Voici un exemple ViewControllerqui l'utilise.

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        let myLabelStackView = ThreeLabelStackView()
        self.view.addSubview(myLabelStackView)

        // Set stackview width to its superview.
        let widthConstraint  = NSLayoutConstraint(item: myLabelStackView, attribute: NSLayoutConstraint.Attribute.width, relatedBy: NSLayoutConstraint.Relation.equal, toItem: self.view, attribute: NSLayoutConstraint.Attribute.width, multiplier: 1, constant: 0)
        self.view.addConstraints([widthConstraint])
    }
}
Zekel
la source
3

Ajouter des UIStackViewpropriétés,

stackView.alignment = .fill
stackView.distribution = .fillProportionally
stackView.spacing = 8.0
stackView.axis = .horizontal

Au lieu d'ajouter une étiquette à l'intérieur UIViewqui n'est pas nécessaire.Si vous l'utilisez à l'intérieur UITableViewCell, rechargez les données lors de la rotation.

Sagar Daundkar
la source
3

Ce qui suit est une implémentation Playground d'une étiquette multiligne avec un saut de ligne à l'intérieur d'un UIStackView. Il ne nécessite pas d'incorporer UILabelquoi que ce soit à l' intérieur et a été testé avec Xcode 9.2 et Swift 4. J'espère que cela sera utile.

import UIKit
import PlaygroundSupport

let containerView = UIView()
containerView.frame = CGRect.init(x: 0, y: 0, width: 400, height: 500)
containerView.backgroundColor = UIColor.white

var label = UILabel.init()
label.textColor = .black
label.numberOfLines = 0
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "This is an example of sample text that goes on for a long time. This is an example of sample text that goes on for a long time."

let stackView = UIStackView.init(arrangedSubviews: [label])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .fill
containerView.addSubview(stackView)
stackView.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: containerView.centerYAnchor).isActive = true
stackView.widthAnchor.constraint(equalTo: containerView.widthAnchor).isActive = true
stackView.heightAnchor.constraint(equalTo: containerView.heightAnchor).isActive = true

PlaygroundPage.current.liveView = containerView
JaredH
la source
1

La mise en page du système doit déterminer l'origine, la largeur et la hauteur pour dessiner les sous-vues, dans ce cas toutes vos sous-vues ont la même priorité, ce point crée un conflit, le système de mise en page ne connaît pas les dépendances entre les vues, que l'on dessine en premier, en deuxième et ainsi de suite

Définir la compression des sous-vues de pile résoudra le problème avec plusieurs lignes, selon que votre vue de pile est horizontale ou verticale et que vous voulez devenir plusieurs lignes. stackOtherSubviews .setContentCompressionResistancePriority(.defaultHight, for: .horizontal) lblTitle.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)

Trung Phan
la source
0

Dans mon cas, j'ai suivi les suggestions précédentes, mais mon texte était toujours tronqué en une seule ligne, mais uniquement en paysage. Il s'avère que j'ai trouvé un \0caractère nul invisible dans le texte de l'étiquette qui était le coupable. Il doit avoir été introduit à côté du symbole de tiret em que j'avais inséré. Pour voir si cela se produit également dans votre cas, utilisez le débogueur de vue pour sélectionner votre étiquette et inspecter son texte.

Jordanie H
la source
0

Xcode 9.2:

Définissez simplement le nombre de lignes sur 0. Storyboard aura l'air bizarre après avoir défini cela. Ne vous inquiétez pas, exécutez le projet. Lors de l'exécution, tout sera redimensionné en fonction de la configuration de votre storyboard. Je pense que c'est une sorte de bug dans le storyboard. entrez la description de l'image ici

Astuces: Réglez le "nombre de doublure à 0" dans le dernier. réinitialisez-le lorsque vous souhaitez modifier une autre vue et réglez-le sur 0 après la modification.

entrez la description de l'image ici

Surendra Kumar
la source
0

Qu'est-ce qui a fonctionné pour moi!

stackview: alignement: remplissage, distribution: remplissage, contrainte largeur proportionnelle à superview ex. 0,8,

label: centre et lignes = 0

Michał Ziobro
la source
0

Pour tous ceux qui ne peuvent toujours pas le faire fonctionner. Essayez de définir la réduction automatique avec une échelle de police minimale sur cet UILabel.

Capture d'écran Paramètres UILabel Autoshrink

Michal Mondík
la source
Pensez à améliorer votre réponse avec un exemple de code.
fridoo le
1
@fridoo Ajout d'une capture d'écran des paramètres.
Michal Mondík
0

C'est presque comme la réponse de @ Andy, mais vous pouvez ajouter votre UILabelen plus UIStackview, verticalement travaillé pour moi.

Zaporozhchenko Oleksandr
la source
0

Pour moi, le problème était que la hauteur de la vue de la pile était tout simplement trop courte. L'étiquette et la vue de la pile ont été correctement configurées pour permettre à l'étiquette d'avoir plusieurs lignes, mais l'étiquette a été la première victime de la compression de contenu utilisée par la vue de la pile pour obtenir une hauteur suffisamment petite.

Luke
la source