Comment ajouter du texte multiligne à un bouton UIB?

368

J'ai le code suivant...

UILabel *buttonLabel = [[UILabel alloc] initWithFrame:targetButton.bounds];
buttonLabel.text = @"Long text string";
[targetButton addSubview:buttonLabel];
[targetButton bringSubviewToFront:buttonLabel];

... l'idée étant que je peux avoir du texte sur plusieurs lignes pour le bouton, mais le texte est toujours obscurci par l'image d'arrière-plan du bouton UIButton. Un appel de journalisation pour afficher les sous-vues du bouton montre que le UILabel a été ajouté, mais le texte lui-même ne peut pas être vu. Est-ce un bug dans UIButton ou est-ce que je fais quelque chose de mal?

Owain Hunt
la source
7
button.titleLabel?.numberOfLines = 0
ma11hew28
2
Remarque pour ce QA très très ancien, dans le Xcode moderne TRÈS SIMPLEMENT, CHOISISSEZ "TEXTE ATTRIBUÉ" et puis c'est trivial, sélectionnez "retour à la ligne".
Fattie
Voir également la réponse mise à jour à une question similaire.
ToolmakerSteve
voir cette réponse (plusieurs lignes, largeur d'ajustement, hauteur d'ajustement) pour le texte du bouton stackoverflow.com/a/59211399/7523163
Hamid Reza Ansari

Réponses:

676

Pour iOS 6 et supérieur, utilisez ce qui suit pour autoriser plusieurs lignes:

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
// you probably want to center it
button.titleLabel.textAlignment = NSTextAlignmentCenter; // if you want to 
[button setTitle: @"Line1\nLine2" forState: UIControlStateNormal];

Pour iOS 5 et versions antérieures, utilisez ce qui suit pour autoriser plusieurs lignes:

button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
// you probably want to center it
button.titleLabel.textAlignment = UITextAlignmentCenter;
[button setTitle: @"Line1\nLine2" forState: UIControlStateNormal];

2017, pour iOS9 en avant ,

généralement, faites simplement ces deux choses:

  1. choisissez "Texte Attribué"
  2. dans le popup "Line Break" sélectionnez "Word Wrap"
jessecurry
la source
5
Veuillez noter que ces affectations sont obsolètes dans iOS 6. Voir la réponse ci-dessous par @NiKKi pour la syntaxe mise à jour.
Aaron Brager
6
Juste pour que les gens sachent: cela ne fonctionne pas si vous utilisez NSAttributedString
Le mec
29
En fait, il suffit d'ajouter button.titleLabel.numberOfLines = 0; Cela rendra les lignes illimitées. Et button.titleLabel.textAlignment = NSTextAlignmentLeft; si vous voulez un texte justifié à gauche car le titre est centré par défaut.
RyJ
4
En brefbutton.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
Robert
1
Merci @Robert pour la solution rapide. Peut également utiliser l' inférence et omettre le NSLineBreakMode, comme suit: button.titleLabel?.lineBreakMode = .ByWordWrapping.
mjswensen
145

La réponse sélectionnée est correcte mais si vous préférez faire ce genre de chose dans Interface Builder, vous pouvez le faire:

pic

Adam Waite
la source
58
Merci à mon moi de 24 ans d'avoir écrit cette réponse, de moi de 28 ans.
Adam Waite
5
Et pour rester avec la configuration d'Interface Builder uniquement, il faut définir titleLabel.textAlignmentavec la Numbervaleur 1in User Defined Runtime Attributedpour rendre le texte centré
AnthoPak
2
Merci à votre moi de 28 ans de mon moi de 24 ans!
Daniel Springer
59

Si vous souhaitez ajouter un bouton avec le titre centré sur plusieurs lignes, définissez les paramètres de votre Interface Builder pour le bouton:

[ ici]

user5440039
la source
Pouvez-vous ajouter une description textuelle de la façon d'y parvenir. Une image est géniale, mais si elle devient indisponible, votre réponse sera inutile.
Rory McCrossan
1
@RoryMcCrossan Salut, j'ai ajouté une image ci-dessus. Pouvez-vous le voir? Veuillez noter que Xcode 7 est toujours bogué, le titre du bouton peut donc disparaître. Cependant, une fois que vous exécutez l'application, vous obtiendrez le résultat souhaité.
user5440039
2
C'est la meilleure réponse dans ce fil, en fait. Fonctionne immédiatement et affiche le texte centré de l'IB. Merci!
RainCast du
@ user5440039 Merci pour la bonne réponse. Il n'est pas nécessaire d'ajouter une description textuelle.
Fattie
54

Pour IOS 6:

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
button.titleLabel.textAlignment = NSTextAlignmentCenter;

Comme

UILineBreakModeWordWrap and UITextAlignmentCenter

sont obsolètes dans IOS 6 et suivants.

NiKKi
la source
A l' intérieur NSText.hde la Foundationil n'y a pas dépréciée ajouté. Son disponible à partir de 6.0 mais pas obsolète.
Alex Cio
En bref: button.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping button.titleLabel?.textAlignment = NSTextAlignment.Center
jcity
30

Pour reformuler la suggestion de Roger Nolan, mais avec un code explicite, voici la solution générale:

button.titleLabel?.numberOfLines = 0
baudot
la source
21

SWIFT 3

button.titleLabel?.lineBreakMode = .byWordWrapping
button.titleLabel?.textAlignment = .center  
button.setTitle("Button\nTitle",for: .normal)
Md. Ibrahim Hassan
la source
13

Dans Xcode 9.3, vous pouvez le faire en utilisant le storyboard comme ci-dessous,

entrez la description de l'image ici

Vous devez définir le texte du titre du bouton Alignement au centre

button.titleLabel?.textAlignment = .center

Vous n'avez pas besoin de définir le texte du titre avec une nouvelle ligne (\ n) comme ci-dessous,

button.setTitle("Good\nAnswer",for: .normal)

Définissez simplement le titre,

button.setTitle("Good Answer",for: .normal)

Voici le résultat,

entrez la description de l'image ici

Nazrul Islam
la source
myButton.titleLabel.textAlignment = NSTextAlignmentCenterest également nécessaire.
tounaobun
11

Il existe un moyen beaucoup plus simple:

someButton.lineBreakMode = UILineBreakModeWordWrap;

(Modifier pour iOS 3 et versions ultérieures :)

someButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
Valerii Hiora
la source
4
UIButton.lineBreakMode a été déconseillé dans 3.0, ce n'est donc plus une bonne option.
Christopher Pickslay
2
lineBreakMode est obsolète uniquement en tant que propriété directe de UIButton. Nous sommes dirigés vers "Utilisez la propriété lineBreakMode de titleLabel à la place." J'ai édité la réponse de Valerii en conséquence. C'est toujours une bonne option.
Wienke
11

Alignement à gauche sur iOS7 avec mise en page automatique:

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
button.titleLabel.textAlignment = NSTextAlignmentLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
neoneye
la source
9

J'ai eu un problème avec la mise en page automatique, après avoir activé plusieurs lignes, le résultat était le suivant: la taille n'affecte donc pas la taille du bouton que j'ai ajouté
entrez la description de l'image ici
titleLabel
Constraints sur la base contentEdgeInsets(dans ce contentEdgeInsets de cas était (10, 10, 10, 10 )
après avoir appelé makeMultiLineSupport(): j'espère que cela vous aidera (swift 5.0):
entrez la description de l'image ici

extension UIButton {

    func makeMultiLineSupport() {
        guard let titleLabel = titleLabel else {
            return
        }
        titleLabel.numberOfLines = 0
        titleLabel.setContentHuggingPriority(.required, for: .vertical)
        titleLabel.setContentHuggingPriority(.required, for: .horizontal)
        addConstraints([
            .init(item: titleLabel,
                  attribute: .top,
                  relatedBy: .greaterThanOrEqual,
                  toItem: self,
                  attribute: .top,
                  multiplier: 1.0,
                  constant: contentEdgeInsets.top),
            .init(item: titleLabel,
                  attribute: .bottom,
                  relatedBy: .greaterThanOrEqual,
                  toItem: self,
                  attribute: .bottom,
                  multiplier: 1.0,
                  constant: contentEdgeInsets.bottom),
            .init(item: titleLabel,
                  attribute: .left,
                  relatedBy: .greaterThanOrEqual,
                  toItem: self,
                  attribute: .left,
                  multiplier: 1.0,
                  constant: contentEdgeInsets.left),
            .init(item: titleLabel,
                  attribute: .right,
                  relatedBy: .greaterThanOrEqual,
                  toItem: self,
                  attribute: .right,
                  multiplier: 1.0,
                  constant: contentEdgeInsets.right)
            ])
    }

}
Amir Khorsandi
la source
Fonctionne parfaitement dans swift 5. J'ai également ajouté ma fonction pour centrer le texte dans le bouton. func centerTextInButton () {guard let titleLabel = titleLabel else {return} titleLabel.textAlignment = .center}
ShadeToD
Je vous remercie. C'est une si bonne solution.
Echelon
J'ai trouvé que l'ajout d'une contrainte n'est pas nécessaire, il suffit de définir le texte une fois que lineBreakMode / textAlignment / numberOfLines sont définis. Et définissez également titleEdgeInsets pour l'encart supérieur et inférieur.
Bill Chan
@BillChan malheureusement, cela ne fonctionne pas dans tous les cas
Amir Khorsandi
7

Tout d'abord, vous devez savoir que UIButton a déjà un UILabel à l'intérieur. Vous pouvez le définir à l'aide de –setTitle:forState:.

Le problème avec votre exemple est que vous devez définir la numberOfLinespropriété UILabel sur autre chose que sa valeur par défaut de 1. Vous devez également revoir la lineBreakModepropriété.

Rog
la source
Je connais la propriété title, mais pour autant que je sache, il est impossible de la configurer pour utiliser plusieurs lignes, d'où cette approche. Si je désactive backgroundImage, mon UILabel apparaît, ce qui me suggère un bogue dans bringSubviewToFront ou UIButton lui-même.
Owain Hunt
4

Pour ceux qui utilisent le storyboard de Xcode 4, vous pouvez cliquer sur le bouton, et dans le volet droit des utilitaires sous Inspecteur d'attributs, vous verrez une option pour le saut de ligne. Choisissez Word Wrap, et vous devriez être prêt à partir.

Jack
la source
4

Les réponses ici vous expliquent comment obtenir un titre de bouton multiligne par programmation.

Je voulais juste ajouter que si vous utilisez des storyboards, vous pouvez taper [Ctrl + Entrée] pour forcer une nouvelle ligne sur un champ de titre de bouton.

HTH

user2338871
la source
4

Vous devez ajouter ce code:

buttonLabel.titleLabel.numberOfLines = 0;
Pablo Blanco
la source
3

Quant à l'idée de Brent de mettre le titre UILabel en vue de frère, elle ne me semble pas être une très bonne idée. Je continue de penser aux problèmes d'interaction avec l'UILabel en raison de ses événements tactiles qui ne passent pas par la vue de l'UIButton.

D'un autre côté, avec un UILabel comme sous-vue du bouton UIButton, je suis assez à l'aise de savoir que les événements tactiles seront toujours propagés à la vue d'ensemble de l'UILabel.

J'ai adopté cette approche et je n'ai remarqué aucun des problèmes signalés avec backgroundImage. J'ai ajouté ce code dans le -titleRectForContentRect: d'une sous-classe UIButton mais le code peut également être placé dans la routine de dessin de la vue d'ensemble UIButton, qui dans ce cas vous devrez remplacer toutes les références à self par la variable UIButton.

#define TITLE_LABEL_TAG 1234

- (CGRect)titleRectForContentRect:(CGRect)rect
{   
    // define the desired title inset margins based on the whole rect and its padding
    UIEdgeInsets padding = [self titleEdgeInsets];
    CGRect titleRect = CGRectMake(rect.origin.x    + padding.left, 
                                  rect.origin.x    + padding.top, 
                                  rect.size.width  - (padding.right + padding.left), 
                                  rect.size.height - (padding.bottom + padding].top));

    // save the current title view appearance
    NSString *title = [self currentTitle];
    UIColor  *titleColor = [self currentTitleColor];
    UIColor  *titleShadowColor = [self currentTitleShadowColor];

    // we only want to add our custom label once; only 1st pass shall return nil
    UILabel  *titleLabel = (UILabel*)[self viewWithTag:TITLE_LABEL_TAG];


    if (!titleLabel) 
    {
        // no custom label found (1st pass), we will be creating & adding it as subview
        titleLabel = [[UILabel alloc] initWithFrame:titleRect];
        [titleLabel setTag:TITLE_LABEL_TAG];

        // make it multi-line
        [titleLabel setNumberOfLines:0];
        [titleLabel setLineBreakMode:UILineBreakModeWordWrap];

        // title appearance setup; be at will to modify
        [titleLabel setBackgroundColor:[UIColor clearColor]];
        [titleLabel setFont:[self font]];
        [titleLabel setShadowOffset:CGSizeMake(0, 1)];
        [titleLabel setTextAlignment:UITextAlignmentCenter];

        [self addSubview:titleLabel];
        [titleLabel release];
    }

    // finally, put our label in original title view's state
    [titleLabel setText:title];
    [titleLabel setTextColor:titleColor];
    [titleLabel setShadowColor:titleShadowColor];

    // and return empty rect so that the original title view is hidden
    return CGRectZero;
}

J'ai pris le temps et j'ai écrit un peu plus à ce sujet ici . Là, je pointe également une solution plus courte, même si elle ne correspond pas tout à fait à tous les scénarios et implique le piratage de certaines vues privées. Vous pouvez également télécharger une sous-classe UIButton prête à être utilisée.

jpedroso
la source
3

Si vous utilisez la mise en page automatique sur iOS 6, vous devrez peut-être également définir la preferredMaxLayoutWidthpropriété:

button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
button.titleLabel.textAlignment = NSTextAlignmentCenter;
button.titleLabel.preferredMaxLayoutWidth = button.frame.size.width;
vially
la source
3

La définition de lineBreakMode sur NSLineBreakByWordWrapping (en IB ou en code) rend l'étiquette de bouton multiligne, mais n'affecte pas le cadre du bouton.

Si le bouton a un titre dynamique, il y a une astuce: mettre UILabel caché avec la même police et lier sa hauteur à la hauteur du bouton avec la mise en page; lorsque le texte est défini sur le bouton et l'étiquette et la mise en page automatique fera tout le travail.

Remarque

La taille de la taille intrinsèque du bouton d'une ligne est plus grande que celle de l'étiquette, donc pour empêcher la hauteur de l'étiquette de rétrécir, la priorité de l'étirement du contenu vertical doit être supérieure à la résistance de compression du contenu vertical du bouton.

Varrry
la source
3

Dans Swift 5.0 et Xcode 10.2

//UIButton extension
extension UIButton {
     //UIButton properties
     func btnMultipleLines() {
         titleLabel?.numberOfLines = 0
         titleLabel?.lineBreakMode = .byWordWrapping
         titleLabel?.textAlignment = .center        
     }
}

Dans votre appel ViewController comme celui-ci

button.btnMultipleLines()//This is your button
iOS
la source
3

Swift 5, pour le texte multi-lignes dans UIButton

  let button = UIButton()
  button.titleLabel?.lineBreakMode = .byWordWrapping
  button.titleLabel?.textAlignment = .center
  button.titleLabel?.numberOfLines = 0 // for Multi line text
Manikandan
la source
2

Cela fonctionne parfaitement.

Ajoutez à utiliser cela avec un fichier de configuration comme Plist, vous devez utiliser CDATA pour écrire le titre multiligne, comme ceci:

<string><![CDATA[Line1
Line2]]></string>
makiko_fly
la source
2

Si vous utilisez la mise en page automatique.

button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.numberOfLines = 2
Sourabh Sharma
la source
2

rapide 4.0

btn.titleLabel?.lineBreakMode = .byWordWrapping
btn.titleLabel?.textAlignment = .center
btn.setTitle( "Line1\nLine2", for: .normal)
ingconti
la source
2

De nos jours, si vous avez vraiment besoin que ce genre de chose soit accessible au cas par cas dans le constructeur d'interface, vous pouvez le faire avec une simple extension comme celle-ci:

extension UIButton {
    @IBInspectable var numberOfLines: Int {
        get { return titleLabel?.numberOfLines ?? 1 }
        set { titleLabel?.numberOfLines = newValue }
    }
}

Ensuite, vous pouvez simplement définir numberOfLines comme attribut sur n'importe quelle sous-classe UIButton ou UIButton comme s'il s'agissait d'une étiquette. Il en va de même pour toute une série d'autres valeurs généralement inaccessibles, telles que le rayon d'angle du calque d'une vue ou les attributs de l'ombre qu'elle projette.

Cendre
la source
0

Lancez votre propre classe de boutons. C'est de loin la meilleure solution à long terme. UIButton et d'autres classes UIKit sont très restrictives dans la façon dont vous pouvez les personnaliser.


la source
0
self.btnError.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping

self.btnError.titleLabel?.textAlignment = .center

self.btnError.setTitle("Title", for: .normal)
Urvish Modi
la source
0

J'ai intégré la réponse de jessecurry dans STAButton qui fait partie de ma bibliothèque open source STAControls . Je l'utilise actuellement dans l'une des applications que je développe et cela fonctionne pour mes besoins. N'hésitez pas à ouvrir des problèmes sur la façon de l'améliorer ou à m'envoyer des demandes d'extraction.

Stunner
la source
0

Pour fixer l'espacement de l'étiquette de titre au bouton, définissez titleEdgeInsets et d'autres propriétés avant setTitle:

    let set = UIButton()
    sut.titleLabel?.lineBreakMode = .byWordWrapping
    sut.titleLabel?.numberOfLines = 0
    sut.titleEdgeInsets = UIEdgeInsets(top: 10, left: 10, bottom: 20, right: 20)
    sut.setTitle("Dummy button with long long long long long long long long title", for: .normal)

PS J'ai testé la définition de titleLabel? .TextAlignment n'est pas nécessaire et le titre s'aligne .natural.

Bill Chan
la source
-4

Bien qu'il soit correct d'ajouter une sous-vue à un contrôle, il n'y a aucune garantie que cela fonctionnera réellement, car le contrôle pourrait ne pas s'attendre à ce qu'il soit là et pourrait donc se comporter mal. Si vous pouvez vous en sortir, ajoutez simplement l'étiquette en tant que vue jumelle du bouton et définissez son cadre de sorte qu'il chevauche le bouton; tant qu'il est configuré pour apparaître en haut du bouton, rien de ce que le bouton peut faire ne l'obscurcira.

En d'autres termes:

[button.superview addSubview:myLabel];
myLabel.center = button.center;
Brent Royal-Gordon
la source
2
S'il vous plaît, si vous suggérez ce type d'approche laide, ne faites pas d'erreurs dans votre exemple de code :). L'étiquette ne doit pas avoir le même centre que le bouton s'il s'agit d'une sous-vue. Utilisez myLabel.frame = button.bounds; ou myLabel.center = cgPointMake (button.frame.size.with * 0.5, button.frame.size.height * 0.5)
JakubKnejzlik
1
@GrizzlyNetch Je conseille spécifiquement de ne pas l'ajouter comme sous-vue du bouton. addSubview:l'ajoute ici à buttonla vue d'ensemble de, ce qui en fait un frère du bouton, donc faire correspondre leurs centres est correct.
Brent Royal-Gordon
Ah, mon erreur! Vous avez raison, j'ai raté le "petit" fait qu'il est dans la superview: D ... désolé :)
JakubKnejzlik
Que ce n'est pas exactement ce qui est demandé, cette solution serait une mauvaise façon de mettre en œuvre la question puisque le bouton d'élément a un titre.
Pablo Blanco
C'était un conseil raisonnable dans l'iPhone OS 2.0. Il existe de bien meilleures façons de le faire maintenant. : ^)
Brent Royal-Gordon