Aligner verticalement le texte en haut dans un UILabel

2175

J'ai un UILabelespace pour deux lignes de texte. Parfois, lorsque le texte est trop court, ce texte s'affiche au centre vertical de l'étiquette.

Comment aligner verticalement le texte pour qu'il soit toujours en haut de la UILabel?

image représentant un UILabel avec du texte centré verticalement

Stefan
la source

Réponses:

2702

Il n'y a aucun moyen de définir l'alignement vertical sur a UILabel, mais vous pouvez obtenir le même effet en modifiant le cadre de l'étiquette. J'ai fait mes étiquettes en orange pour que vous puissiez voir clairement ce qui se passe.

Voici le moyen rapide et facile de le faire:

    [myLabel sizeToFit];

sizeToFit pour presser une étiquette


Si vous avez une étiquette avec un texte plus long qui fera plus d'une ligne, définissez numberOfLinessur 0(zéro ici signifie un nombre illimité de lignes).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Texte d'étiquette plus long avec sizeToFit


Version plus longue

Je vais créer mon étiquette en code afin que vous puissiez voir ce qui se passe. Vous pouvez également configurer la plupart de cela dans Interface Builder. Ma configuration est une application basée sur la vue avec une image d'arrière-plan que j'ai créée dans Photoshop pour afficher les marges (20 points). L'étiquette est d'une couleur orange attrayante pour que vous puissiez voir ce qui se passe avec les dimensions.

- (void)viewDidLoad
{
    [super viewDidLoad];

    // 20 point top and left margin. Sized to leave 20 pt at right.
    CGRect labelFrame = CGRectMake(20, 20, 280, 150);
    UILabel *myLabel = [[UILabel alloc] initWithFrame:labelFrame];
    [myLabel setBackgroundColor:[UIColor orangeColor]];

    NSString *labelText = @"I am the very model of a modern Major-General, I've information vegetable, animal, and mineral";
    [myLabel setText:labelText];

    // Tell the label to use an unlimited number of lines
    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

    [self.view addSubview:myLabel];
}

Certaines limites d'utilisation sizeToFitentrent en jeu avec le texte aligné au centre ou à droite. Voici ce qui se passe:

    // myLabel.textAlignment = NSTextAlignmentRight;
    myLabel.textAlignment = NSTextAlignmentCenter;

    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

entrez la description de l'image ici

L'étiquette est toujours dimensionnée avec un coin supérieur gauche fixe. Vous pouvez enregistrer la largeur de l'étiquette d'origine dans une variable et la définir après sizeToFit, ou lui donner une largeur fixe pour contrer ces problèmes:

    myLabel.textAlignment = NSTextAlignmentCenter;

    [myLabel setNumberOfLines:0];
    [myLabel sizeToFit];

    CGRect myFrame = myLabel.frame;
    // Resize the frame's width to 280 (320 - margins)
    // width could also be myOriginalLabelFrame.size.width
    myFrame = CGRectMake(myFrame.origin.x, myFrame.origin.y, 280, myFrame.size.height);
    myLabel.frame = myFrame;

alignement des étiquettes


Notez que sizeToFitcela respectera la largeur minimale de votre étiquette initiale. Si vous commencez avec une étiquette de 100 de large et que vous l'appelez sizeToFit, cela vous donnera une étiquette (peut-être très grande) de 100 (ou un peu moins) de largeur. Vous souhaiterez peut-être définir votre étiquette à la largeur minimale souhaitée avant de redimensionner.

Corriger l'alignement des étiquettes en redimensionnant la largeur du cadre

Quelques autres choses à noter:

Le lineBreakModerespect dépend de la façon dont il est défini. NSLineBreakByTruncatingTail(par défaut) est ignoré après sizeToFit, de même que les deux autres modes de troncature (tête et milieu). NSLineBreakByClippingest également ignoré. NSLineBreakByCharWrappingfonctionne comme d'habitude. La largeur du cadre est toujours réduite pour s'adapter à la lettre la plus à droite.


Mark Amery a donné un correctif pour les NIB et les storyboards utilisant la disposition automatique dans les commentaires:

Si votre étiquette est incluse dans une plume ou un storyboard en tant que sous-vue d' viewun ViewController qui utilise la mise en page automatique, alors placer votre sizeToFitappel viewDidLoadne fonctionnera pas, car la mise en page automatique taille et positionne les sous-vues après viewDidLoadest appelée et annulera immédiatement les effets de votre sizeToFitappel. Toutefois, l' appel sizeToFitde l' intérieur viewDidLayoutSubviews volonté travail.


Ma réponse originale (pour la postérité / référence):

Cela utilise la NSStringméthode sizeWithFont:constrainedToSize:lineBreakMode:pour calculer la hauteur du cadre nécessaire pour s'adapter à une chaîne, puis définit l'origine et la largeur.

Redimensionnez le cadre de l'étiquette en utilisant le texte que vous souhaitez insérer. De cette façon, vous pouvez accueillir n'importe quel nombre de lignes.

CGSize maximumSize = CGSizeMake(300, 9999);
NSString *dateString = @"The date today is January 1st, 1999";
UIFont *dateFont = [UIFont fontWithName:@"Helvetica" size:14];
CGSize dateStringSize = [dateString sizeWithFont:dateFont 
        constrainedToSize:maximumSize 
        lineBreakMode:self.dateLabel.lineBreakMode];

CGRect dateFrame = CGRectMake(10, 10, 300, dateStringSize.height);

self.dateLabel.frame = dateFrame;
nevan king
la source
10
vous devez supprimer autolayout
hungbm06
4
Voici un moyen simple de résoudre le problème: stackoverflow.com/a/26632490/636559
AboulEinein
Si vous utilisez la mise en page automatique et le storyboard, l'ajout d'une contrainte avec l'option "Supprimer au moment de la construction" cochée aidera
JK ABC
Si vous en avez besoin, adjustsFontSizeToFitWidthutilisez alors sizeToFit, puis définissez le cadre de l'étiquette sur 0, 0, theWidthYouWant, label.frame.size.height (qui est la nouvelle hauteur déterminée par sizeToFit) ALORS appliqueradjustsFontSizeToFitWidth
Albert Renshaw
3
Cette réponse est inutilement compliquée et elle utilise un "mot autour". Veuillez voir la suggestion ci-dessous de simplement changer la priorité de l'étirement du contenu vertical. Ne nécessite aucun code ...
beetree
335
  1. Définissez le nouveau texte:

    myLabel.text = @"Some Text"
  2. Réglez les maximum numberlignes sur 0 (automatique):

    myLabel.numberOfLines = 0
  3. Réglez le cadre de l'étiquette à la taille maximale:

    myLabel.frame = CGRectMake(20,20,200,800)
  4. Appelez sizeToFitpour réduire la taille du cadre afin que le contenu corresponde:

    [myLabel sizeToFit]

Le cadre des étiquettes est maintenant juste assez haut et large pour s'adapter à votre texte. Le coin supérieur gauche doit rester inchangé. J'ai testé cela uniquement avec le texte aligné en haut à gauche. Pour les autres alignements, vous devrez peut-être modifier le cadre par la suite.

En outre, mon étiquette a activé l'habillage de mots.

Jakob Egger
la source
Hé Ben, je viens de revoir mon ancien code et j'ai mis à jour ma réponse avec les étapes correctes nécessaires.
Jakob Egger
CA marchait bien pour moi. J'ai défini les dimensions de mon UILabel et changé numberOfLines à 0 dans IB, puis après avoir défini le texte appelé [myLabel sizeToFit].
Dan Ellis
fonctionne parfaitement pour moi, après avoir défini le nombre de lignes dans Attr. Inspecteur
d2burke
Ne fonctionne pas en cas de nombre de lignes supérieur à 1
Tom
Cela ne fonctionne pas lorsque vous souhaitez une étiquette de deux lignes, mais le texte nécessite plus de lignes.
Jose Manuel Abarca Rodríguez
159

En référence à la solution d'extension:

for(int i=1; i< newLinesToPad; i++) 
    self.text = [self.text stringByAppendingString:@"\n"];

devrait être remplacé par

for(int i=0; i<newLinesToPad; i++)
    self.text = [self.text stringByAppendingString:@"\n "];

Un espace supplémentaire est nécessaire dans chaque nouvelle ligne ajoutée, car UILabelsles retours de chariot de l' iPhone semblent être ignorés :(

De même, alignBottom doit également être mis à jour avec un @" \n@%"à la place de "\n@%"(pour l'initialisation du cycle doit être remplacé par "for (int i = 0 ..." aussi).

L'extension suivante fonctionne pour moi:

// -- file: UILabel+VerticalAlign.h
#pragma mark VerticalAlign
@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end

// -- file: UILabel+VerticalAlign.m
@implementation UILabel (VerticalAlign)
- (void)alignTop {
    CGSize fontSize = [self.text sizeWithFont:self.font];
    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label
    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;
    for(int i=0; i<newLinesToPad; i++)
        self.text = [self.text stringByAppendingString:@"\n "];
}

- (void)alignBottom {
    CGSize fontSize = [self.text sizeWithFont:self.font];
    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label
    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];
    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;
    for(int i=0; i<newLinesToPad; i++)
        self.text = [NSString stringWithFormat:@" \n%@",self.text];
}
@end

Appelez ensuite [yourLabel alignTop];ou [yourLabel alignBottom];après chaque affectation de texte yourLabel.

Dmitri Sologoubenko
la source
@DS J'ai remarqué que vous ajoutez VerticalAlignentre parenthèses après @implementation UILabel. Étant nouveau dans Objective-C, je n'avais jamais rencontré cette syntaxe auparavant. Comment ça s'appelle?
Julian
Incroyable, je ne connaissais pas les catégories, cela me donne encore plus d'appréciation pour le langage Objective-c. Apprendre plus de langues est si important pour devenir un bon programmeur.
JeroenEijkhof
A donné un vote positif. mais iirc cela ne fonctionnera pas si vous ne connaissez pas le nombre de lignes, ce qui est un inconvénient
Tjirp
Je dois montrer 3 lignes de texte et aligner le texte en haut. Donc, dois-je définir le nombre de lignes à 3. Lorsque je le mets à 3, je vois "..." si le texte est moins (qui tient sur une seule ligne). Comment éviter ce "..."
Satyam
1
J'ai posté une modification de cette catégorie, j'espère qu'elle sera approuvée sous peu. Les méthodes sizeWithFont:constrainedToSize:lineBreakMode:et sizeWithFont: sont toutes deux dépréciées dans iOS7. En outre, cette catégorie ne fonctionne que sur les étiquettes lorsque numberOfLines est supérieur à 0.
timgcarlson
146

Juste au cas où cela aiderait quelqu'un, j'ai eu le même problème mais j'ai pu résoudre le problème simplement en passant de l'utilisation UILabelà l'utilisation UITextView. J'apprécie que ce n'est pas pour tout le monde car la fonctionnalité est un peu différente.

Si vous passez à l'utilisation UITextView, vous pouvez désactiver toutes les propriétés de la vue de défilement ainsi que l'interaction utilisateur activée ... Cela l'obligera à agir davantage comme une étiquette.

entrez la description de l'image ici

jowie
la source
1
Vous ne savez pas comment cela affectera les performances si de nombreux UILabels sont convertis en vues de texte.
ninjaneer
2
Toutes les autres solutions sur ce fil ne fonctionnaient pas pour moi dans iOS 7 (pour une raison quelconque). Mais simplement utiliser un UITextViewm'a donné le résultat souhaité tout de suite.
Clifton Labrum
1
C'est une solution de contournement, il a encore besoin de quelques ajustements pour le rendre authentique, mais en effet c'est la solution sans code la plus simple que j'ai vue, bravo.
Itai Spector
1
J'aime cette solution. Certes, il peut y avoir des problèmes de performances et ainsi, pour une simple étiquette sur une vue relativement simple, c'était parfait pour ce dont j'avais besoin (et je ne remarque aucun impact sur les performances)
JCutting8
1
Un petit inconvénient de cette méthode est qu'elle introduira un remplissage intrinsèque supplémentaire dans le texte. Une solution rapide consiste à introduire une contrainte négative.
Sira Lam
122

Pas de bruit, pas de bruit

@interface MFTopAlignedLabel : UILabel

@end


@implementation MFTopAlignedLabel

- (void)drawTextInRect:(CGRect) rect
{
    NSAttributedString *attributedText = [[NSAttributedString alloc]     initWithString:self.text attributes:@{NSFontAttributeName:self.font}];
    rect.size.height = [attributedText boundingRectWithSize:rect.size
                                            options:NSStringDrawingUsesLineFragmentOrigin
                                            context:nil].size.height;
    if (self.numberOfLines != 0) {
        rect.size.height = MIN(rect.size.height, self.numberOfLines * self.font.lineHeight);
    }
    [super drawTextInRect:rect];
}

@end

Pas de bruit, pas d'Objective-c, pas de bruit mais Swift 3:

class VerticalTopAlignLabel: UILabel {

    override func drawText(in rect:CGRect) {
        guard let labelText = text else {  return super.drawText(in: rect) }

        let attributedText = NSAttributedString(string: labelText, attributes: [NSFontAttributeName: font])
        var newRect = rect
        newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

        if numberOfLines != 0 {
            newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
        }

        super.drawText(in: newRect)
    }

}

Swift 4.2

class VerticalTopAlignLabel: UILabel {

    override func drawText(in rect:CGRect) {
        guard let labelText = text else {  return super.drawText(in: rect) }

        let attributedText = NSAttributedString(string: labelText, attributes: [NSAttributedString.Key.font: font])
        var newRect = rect
        newRect.size.height = attributedText.boundingRect(with: rect.size, options: .usesLineFragmentOrigin, context: nil).size.height

        if numberOfLines != 0 {
            newRect.size.height = min(newRect.size.height, CGFloat(numberOfLines) * font.lineHeight)
        }

        super.drawText(in: newRect)
    }

}
jasongregori
la source
La version rapide a fonctionné pour moi sans aucun effet secondaire! Bon travail!
l.vasilev
2
Probablement la meilleure réponse ici, fonctionne dans tous les scénarios. sizeToFit ne fonctionne pas si l'étiquette est dans UITableviewCell. Bon travail.
rajat chauhan
79

Comme la réponse ci-dessus, mais ce n'était pas tout à fait correct, ou facile à insérer dans le code, donc je l'ai nettoyé un peu. Ajoutez cette extension à ses propres fichiers .h et .m ou collez-la juste au-dessus de l'implémentation que vous souhaitez utiliser:

#pragma mark VerticalAlign
@interface UILabel (VerticalAlign)
- (void)alignTop;
- (void)alignBottom;
@end


@implementation UILabel (VerticalAlign)
- (void)alignTop
{
    CGSize fontSize = [self.text sizeWithFont:self.font];

    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label


    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];


    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

    for(int i=0; i<= newLinesToPad; i++)
    {
        self.text = [self.text stringByAppendingString:@" \n"];
    }
}

- (void)alignBottom
{
    CGSize fontSize = [self.text sizeWithFont:self.font];

    double finalHeight = fontSize.height * self.numberOfLines;
    double finalWidth = self.frame.size.width;    //expected width of label


    CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];


    int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

    for(int i=0; i< newLinesToPad; i++)
    {
        self.text = [NSString stringWithFormat:@" \n%@",self.text];
    }
}
@end

Et puis pour utiliser, mettez votre texte dans l'étiquette, puis appelez la méthode appropriée pour l'aligner:

[myLabel alignTop];

ou

[myLabel alignBottom];
BadPirate
la source
sizeWithFont déconseillé
tdios
68

Un moyen encore plus rapide (et plus sale) d'accomplir cela est de définir le mode de saut de ligne de l'UILabel sur "Clip" et d'ajouter une quantité fixe de nouvelles lignes.

myLabel.lineBreakMode = UILineBreakModeClip;
myLabel.text = [displayString stringByAppendingString:"\n\n\n\n"];

Cette solution ne fonctionnera pas pour tout le monde - en particulier, si vous souhaitez toujours afficher "..." à la fin de votre chaîne si elle dépasse le nombre de lignes que vous affichez, vous devrez utiliser l'une des les morceaux de code les plus longs - mais pour de nombreux cas, cela vous fournira ce dont vous avez besoin.

Purple Ninja Girl
la source
3
Régler le mode de saut de ligne sur «clip» semble perturber le dimensionnement automatique de l'étiquette. Utilisez UILineBreakModeWordWrapplutôt.
Niels van der Rest
et mieux ajouter des espaces "\ n \ n"
djdance
68

Approche la plus simple avec Storyboard:

Incorporez une étiquette dans StackView et définissez les deux attributs suivants de StackView dans l'inspecteur d'attributs:

1- Axisà Horizontal,

2- AlignmentàTop

entrez la description de l'image ici

Baig
la source
1
Pour de meilleurs résultats, vous pouvez le faire StackView> Affichage> UILabel, car lorsque vous venez d'incorporer un UILabel dans un StackView, le StackView redimensionner pour adapter UILabel à la taille minimale
Daniel Beltrami
Excellente idée à placer UILabeldans une UIViewsous-classe ( UIViewordinaire suffit généralement) et laisser cette étiquette se développer dans le sens descendant. Merci!
Viktor Kucera
1
Impressionnant! Il convient également de mentionner que vous devez supprimer les contraintes sur UILabelet laisser le StackView faire son travail. Merci!
Luiz Dias
Avec la commande de menu Editeur-> Incorporer avec votre étiquette sélectionnée, les contraintes d'étiquettes sont effacées pour vous par Xcode, vous pouvez alors simplement définir les contraintes pour StackView. Cette approche est la plus proche d'une approche de mise en page «SwiftUI», c'est ainsi que je l'ai découverte.
Rocket Garden
pourquoi ça marche?
Novellizator
60

Au lieu de UILabelvous pouvez utiliser UITextFieldce qui a l'option d'alignement vertical:

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
ivanzoïde
la source
7
Avertissement: UITextField n'autorise qu'une seule ligne de texte.
David James
2
Pour compléter le commentaire @DavidJames: UITextView serait plus approprié
CedricSoubrie
49

Je lutte depuis longtemps avec celui-ci et je voulais partager ma solution.

Cela vous donnera un UILabeltexte qui rétrécira automatiquement jusqu'à 0,5 échelle et centrera verticalement le texte. Ces options sont également disponibles dans Storyboard / IB.

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
David Greco
la source
Cela a fonctionné comme parfait parmi toutes les réponses fournies. Merci @David Greco. Parallèlement à ces propriétés, si nous définissons adjustsFontSizeToFitWidth sur YES, le texte s'insérera dans le cadre sans modifier le cadre de l'étiquette.
Ashok
43

Créer une nouvelle classe

LabelTopAlign

fichier .h

#import <UIKit/UIKit.h>


@interface KwLabelTopAlign : UILabel {

}

@end

fichier .m

#import "KwLabelTopAlign.h"


@implementation KwLabelTopAlign

- (void)drawTextInRect:(CGRect)rect {
    int lineHeight = [@"IglL" sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, 9999.0f)].height;
    if(rect.size.height >= lineHeight) {
        int textHeight = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(rect.size.width, rect.size.height)].height;
        int yMax = textHeight;
        if (self.numberOfLines > 0) {
            yMax = MIN(lineHeight*self.numberOfLines, yMax);    
        }

        [super drawTextInRect:CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, yMax)];
    }
}

@end

Éditer

Voici une implémentation plus simple qui fait de même:

#import "KwLabelTopAlign.h"

@implementation KwLabelTopAlign

- (void)drawTextInRect:(CGRect)rect
{
    CGFloat height = [self.text sizeWithFont:self.font
                            constrainedToSize:rect.size
                                lineBreakMode:self.lineBreakMode].height;
    if (self.numberOfLines != 0) {
        height = MIN(height, self.font.lineHeight * self.numberOfLines);
    }
    rect.size.height = MIN(rect.size.height, height);
    [super drawTextInRect:rect];
}

@end
sebastian friedrich
la source
+1 pour la vraie réponse. Contrairement aux sizeToFitsolutions ce qui rend en fait la UILabelmettre tout texte en haut, même si vous remplacez dynamiquement un texte plus court avec une plus ou vice versa.
SnakE
38

entrez la description de l'image ici

Dans Interface Builder

  • Ensemble UILabel à la taille du plus grand texte possible
  • Défini Linessur «0» dans l'inspecteur d'attributs

Dans votre code

  • Définissez le texte de l'étiquette
  • Appelez sizeToFitsur votre étiquette

Extrait de code:

self.myLabel.text = @"Short Title";
[self.myLabel sizeToFit];
oursMontagne
la source
a très bien fonctionné, sauf que dans mon cas, je devais définir des lignes à 2 - avoir un UILabel à l'intérieur d'un UITableViewCell et le réglage à 0 l'a fait se chevaucher, mais le réglage à 2 a fonctionné (la cellule a assez de place pour 2 lignes)
eselk
34

Pour l'interface utilisateur adaptative (iOS8 ou version ultérieure), l'alignement vertical de UILabel doit être défini à partir de StoryBoard en modifiant les propriétés noOfLines= 0 et

Contraintes

  1. Ajustement des contraintes UILabel LefMargin, RightMargin et Top Margin.

  2. Changement Content Compression Resistance Priority For Vertical = 1000` Pour que Vertical> Horizontal.

Contraintes de UILabel

Édité:

noOfLines=0

et les contraintes suivantes sont suffisantes pour obtenir les résultats souhaités.

entrez la description de l'image ici

Rabindra Nath Nandi
la source
C'était génial. Pouvez-vous expliquer pourquoi le vertical> horizontal a fait le travail? Merci.
Gabriel
33

Créez une sous-classe de UILabel. Fonctionne comme un charme:

// TopLeftLabel.h

#import <Foundation/Foundation.h>

@interface TopLeftLabel : UILabel 
{
}

@end

// TopLeftLabel.m

#import "TopLeftLabel.h"

@implementation TopLeftLabel

- (id)initWithFrame:(CGRect)frame 
{
    return [super initWithFrame:frame];
}

- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines 
{
    CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];    
    textRect.origin.y = bounds.origin.y;
    return textRect;
}

-(void)drawTextInRect:(CGRect)requestedRect 
{
    CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
    [super drawTextInRect:actualRect];
}

@end

Comme discuté ici .

Martin Wickman
la source
27

J'ai écrit une fonction util pour atteindre cet objectif. Vous pouvez jeter un œil:

// ajuste la hauteur d'une étiquette multi-lignes pour l'aligner verticalement avec le haut
+ (void) alignLabelWithTop: (UILabel *) label {
  CGSize maxSize = CGSizeMake (label.frame.size.width, 999);
  label.adjustsFontSizeToFitWidth = NO;

  // obtenir la hauteur réelle
  CGSize actualSize = [label.text sizeWithFont: label.font constrainedToSize: maxSize lineBreakMode: label.lineBreakMode];
  CGRect rect = label.frame;
  rect.size.height = actualSize.height;
  label.frame = rect;
}

.Comment utiliser? (Si lblHello est créé par le générateur d'interface, je saute donc certains détails des attributs UILabel)

lblHello.text = @ "Bonjour tout le monde! Bonjour tout le monde! Bonjour tout le monde! Bonjour tout le monde! Bonjour tout le monde! Bonjour tout le monde! Bonjour tout le monde! Bonjour tout le monde!";
lblHello.numberOfLines = 5;
[Utils alignLabelWithTop: lblHello];

Je l'ai également écrit sur mon blog en tant qu'article : http://fstoke.me/blog/?p=2819

firestoke
la source
27

J'ai pris un certain temps pour lire le code, ainsi que le code de la page introduite, et j'ai constaté qu'ils essayaient tous de modifier la taille du cadre de l'étiquette, afin que l'alignement vertical central par défaut n'apparaisse pas.

Cependant, dans certains cas, nous voulons que l'étiquette occupe tous ces espaces, même si l'étiquette contient beaucoup de texte (par exemple, plusieurs lignes de même hauteur).

Ici, j'ai utilisé une autre façon de le résoudre, en remplissant simplement les sauts de ligne jusqu'à la fin de l'étiquette (veuillez noter que j'ai hérité de la UILabel, mais ce n'est pas nécessaire):

CGSize fontSize = [self.text sizeWithFont:self.font];

finalHeight = fontSize.height * self.numberOfLines;
finalWidth = size.width;    //expected width of label

CGSize theStringSize = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(finalWidth, finalHeight) lineBreakMode:self.lineBreakMode];

int newLinesToPad = (finalHeight  - theStringSize.height) / fontSize.height;

for(int i = 0; i < newLinesToPad; i++)
{
    self.text = [self.text stringByAppendingString:@"\n "];
}
Walty Yeung
la source
22

Ce que j'ai fait dans mon application était de définir la propriété de ligne de l'UILabel sur 0 ainsi que de créer une contrainte inférieure de l'UILabel et de s'assurer qu'elle est définie sur> = 0, comme indiqué dans l'image ci-dessous. entrez la description de l'image ici

Jack Tiong
la source
19

J'ai pris les suggestions ici et créé une vue qui peut envelopper un UILabel et le dimensionner et définir le nombre de lignes afin qu'il soit aligné en haut. Mettez simplement un UILabel comme sous-vue:

@interface TopAlignedLabelContainer : UIView
{
}

@end

@implementation TopAlignedLabelContainer

- (void)layoutSubviews
{
    CGRect bounds = self.bounds;

    for (UILabel *label in [self subviews])
    {
        if ([label isKindOfClass:[UILabel class]])
        {
            CGSize fontSize = [label.text sizeWithFont:label.font];

            CGSize textSize = [label.text sizeWithFont:label.font
                                     constrainedToSize:bounds.size
                                         lineBreakMode:label.lineBreakMode];

            label.numberOfLines = textSize.height / fontSize.height;

            label.frame = CGRectMake(0, 0, textSize.width,
                 fontSize.height * label.numberOfLines);
        }
    }
}

@end

la source
19

Vous pouvez utiliser TTTAttributedLabel , il prend en charge l'alignement vertical.

@property (nonatomic) TTTAttributedLabel* label;
<...>

//view's or viewController's init method
_label.verticalAlignment = TTTAttributedLabelVerticalAlignmentTop;
Andrew Romanov
la source
16

J'ai trouvé que les réponses à cette question sont maintenant un peu obsolètes, alors ajoutez cela pour les fans de mise en page automatique.

La mise en page automatique rend ce problème assez trivial. En supposant que nous ajoutons l'étiquette à UIView *view, le code suivant accomplira cela:

UILabel *label = [[UILabel alloc] initWithFrame:CGRectZero];
[label setText:@"Some text here"];
[label setTranslatesAutoresizingMaskIntoConstraints:NO];
[view addSubview:label];

[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[label]|" options:0 metrics:nil views:@{@"label": label}]];
[view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|[label]" options:0 metrics:nil views:@{@"label": label}]];

La hauteur de l'étiquette sera calculée automatiquement (en l'utilisant intrinsicContentSize) et l'étiquette sera positionnée bord à bord horizontalement, en haut de la view.

petehare
la source
15

J'ai utilisé beaucoup des méthodes ci-dessus, et je veux juste ajouter une approche rapide et sale que j'ai utilisée:

myLabel.text = [NSString stringWithFormat:@"%@\n\n\n\n\n\n\n\n\n",@"My label text string"];

Assurez-vous que le nombre de sauts de ligne dans la chaîne entraînera tout texte à remplir l'espace vertical disponible et définissez le UILabel pour tronquer tout texte débordant.

Parce que parfois assez bon est assez bon .

Code Roadie
la source
Cependant, étant donné la variation des hauteurs de taille d'écran des appareils iOS, il faudrait alors un nombre variable de '\ n' pour tenir compte de la hauteur de changement d'étiquette (ce qui est une possibilité). Cette méthode n'est donc pas particulièrement utile à long terme.
Rambatino
Remarque: "rapide et sale" pas "une solution parfaite pour tous les temps." Je dis juste.
Code Roadie du
2
@CodeRoadie Rapide et sale a fait l'affaire pour moi, j'ai eu quelques problèmes de mise en page avec la vraie réponse. Je pourrais le faire de la "bonne façon", mais merci de m'avoir fait gagner du temps!
Séraphin Hochart
@dGambit s'il vous plaît noter: "rapide et sale "
Code Roadie
1
Quelque chose que j'ai appris récemment lors du chargement dynamique des vues, UITextViewc'est qu'il peut appeler dlopenafin de prendre en charge la saisie de texte. Cela peut entraîner un retard important sur le thread d'interface utilisateur, donc cette approche est beaucoup plus performante!
yano
14

Je voulais avoir une étiquette capable d'avoir plusieurs lignes, une taille de police minimale et centrée horizontalement et verticalement dans sa vue parent. J'ai ajouté mon étiquette par programme à ma vue:

- (void) customInit {
    // Setup label
    self.label = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    self.label.numberOfLines = 0;
    self.label.lineBreakMode = UILineBreakModeWordWrap;
    self.label.textAlignment = UITextAlignmentCenter;

    // Add the label as a subview
    self.autoresizesSubviews = YES;
    [self addSubview:self.label];
}

Et puis quand j'ai voulu changer le texte de mon étiquette ...

- (void) updateDisplay:(NSString *)text {
    if (![text isEqualToString:self.label.text]) {
        // Calculate the font size to use (save to label's font)
        CGSize textConstrainedSize = CGSizeMake(self.frame.size.width, INT_MAX);
        self.label.font = [UIFont systemFontOfSize:TICKER_FONT_SIZE];
        CGSize textSize = [text sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
        while (textSize.height > self.frame.size.height && self.label.font.pointSize > TICKER_MINIMUM_FONT_SIZE) {
            self.label.font = [UIFont systemFontOfSize:self.label.font.pointSize-1];
            textSize = [ticker.blurb sizeWithFont:self.label.font constrainedToSize:textConstrainedSize];
        }
        // In cases where the frame is still too large (when we're exceeding minimum font size),
        // use the views size
        if (textSize.height > self.frame.size.height) {
            textSize = [text sizeWithFont:self.label.font constrainedToSize:self.frame.size];
        }

        // Draw 
        self.label.frame = CGRectMake(0, self.frame.size.height/2 - textSize.height/2, self.frame.size.width, textSize.height);
        self.label.text = text;
    }
    [self setNeedsDisplay];
}

J'espère que cela aide quelqu'un!

Johnus
la source
14

FXLabel (sur github) le fait hors de la boîte en définissant label.contentModesur UIViewContentModeTop. Ce composant n'est pas fait par moi, mais c'est un composant que j'utilise fréquemment et qui a des tonnes de fonctionnalités, et semble bien fonctionner.

jjxtra
la source
11

pour quiconque lit ceci parce que le texte à l'intérieur de votre étiquette n'est pas centré verticalement, gardez à l'esprit que certains types de polices ne sont pas conçus de la même manière. par exemple, si vous créez une étiquette de taille zapfino 16, vous verrez que le texte n'est pas parfaitement centré verticalement.

cependant, travailler avec helvetica centrera verticalement votre texte.

Nir Pengas
la source
De nombreuses polices personnalisées ne sont pas alignées verticalement au centre. UILabel ou UITextView sont toujours alignés au centre vertical. Pas besoin de penser à l'alignement vertical dans la programmation iOS.
Amit Singh
11

Utilisez textRect(forBounds:limitedToNumberOfLines:).

class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        let textRect = super.textRect(forBounds: bounds, limitedToNumberOfLines: numberOfLines)
        super.drawText(in: textRect)
    }
}
ma11hew28
la source
Vous monsieur êtes un dieu!
Diogo Antunes
1
Cette réponse devrait obtenir beaucoup plus de votes positifs car c'est de loin la solution la plus agréable et la plus propre!
Michael Ochs
10

Sous-classe UILabel et contraignez le rectangle de dessin, comme ceci:

- (void)drawTextInRect:(CGRect)rect
{
    CGSize sizeThatFits = [self sizeThatFits:rect.size];
    rect.size.height = MIN(rect.size.height, sizeThatFits.height);

    [super drawTextInRect:rect];
}

J'ai essayé la solution impliquant un remplissage de nouvelle ligne et j'ai rencontré un comportement incorrect dans certains cas. D'après mon expérience, il est plus facile de contraindre le rect rectangle comme ci-dessus que de jouer avec numberOfLines.

PS Vous pouvez imaginer facilement supporter UIViewContentMode de cette façon:

- (void)drawTextInRect:(CGRect)rect
{
    CGSize sizeThatFits = [self sizeThatFits:rect.size];

    if (self.contentMode == UIViewContentModeTop) {
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    }
    else if (self.contentMode == UIViewContentModeBottom) {
        rect.origin.y = MAX(0, rect.size.height - sizeThatFits.height);
        rect.size.height = MIN(rect.size.height, sizeThatFits.height);
    }

    [super drawTextInRect:rect];
}
jrc
la source
10

Si vous utilisez la mise en page automatique, définissez le contentHuggingPriority vertical sur 1000, soit en code, soit en IB. Dans IB, vous devrez peut-être supprimer une contrainte de hauteur en définissant sa priorité sur 1, puis en la supprimant.

phatmann
la source
8

Tant que vous n'effectuez aucune tâche complexe, vous pouvez utiliser à la UITextViewplace de UILabels.

Désactivez le défilement.

Si vous voulez que le texte soit affiché complètement juste l'utilisateur sizeToFitet les sizeThatFits:méthodes

le signe
la source
8

En bref,

let myLabel : UILabel!

Pour que votre texte de votre étiquette s'adapte à l'écran et qu'il se trouve en haut

myLabel.sizeToFit()

Pour que votre police d'étiquette s'adapte à la largeur de l'écran ou à une largeur spécifique.

myLabel.adjustsFontSizeToFitWidth = YES

et certains textAlignment pour l'étiquette:

myLabel.textAlignment = .center

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified

Dara Tith
la source
Juste un changement de syntaxe par rapport à l'ancienne syntaxe de [myLabel sizeToFit]. Je vous remercie!
velkoon
7

Ceci est une ancienne solution, utilisez la mise en page automatique sur iOS> = 6

Ma solution:

  1. Séparer les lignes par moi-même (en ignorant les paramètres de retour à la ligne des étiquettes)
  2. Tracer des lignes par moi-même (en ignorant l'alignement des étiquettes)

@interface UITopAlignedLabel : UILabel

@end

@implementation UITopAlignedLabel

#pragma mark Instance methods

- (NSArray*)splitTextToLines:(NSUInteger)maxLines {
    float width = self.frame.size.width;

    NSArray* words = [self.text componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
    NSMutableArray* lines = [NSMutableArray array];

    NSMutableString* buffer = [NSMutableString string];    
    NSMutableString* currentLine = [NSMutableString string];

    for (NSString* word in words) {
        if ([buffer length] > 0) {
            [buffer appendString:@" "];
        }

        [buffer appendString:word];

        if (maxLines > 0 && [lines count] == maxLines - 1) {
            [currentLine setString:buffer];
            continue;
        }

        float bufferWidth = [buffer sizeWithFont:self.font].width;

        if (bufferWidth < width) {
            [currentLine setString:buffer];
        }
        else {
            [lines addObject:[NSString stringWithString:currentLine]];

            [buffer setString:word];
            [currentLine setString:buffer];
        }
    }

    if ([currentLine length] > 0) {
        [lines addObject:[NSString stringWithString:currentLine]];
    }

    return lines;
}

- (void)drawRect:(CGRect)rect {
    if ([self.text length] == 0) {
        return;
    }

    CGContextRef context = UIGraphicsGetCurrentContext();

    CGContextSetFillColorWithColor(context, self.textColor.CGColor);
    CGContextSetShadowWithColor(context, self.shadowOffset, 0.0f, self.shadowColor.CGColor);

    NSArray* lines = [self splitTextToLines:self.numberOfLines];
    NSUInteger numLines = [lines count];

    CGSize size = self.frame.size;
    CGPoint origin = CGPointMake(0.0f, 0.0f);

    for (NSUInteger i = 0; i < numLines; i++) {
        NSString* line = [lines objectAtIndex:i];

        if (i == numLines - 1) {
            [line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeTailTruncation];            
        }
        else {
            [line drawAtPoint:origin forWidth:size.width withFont:self.font lineBreakMode:UILineBreakModeClip];
        }

        origin.y += self.font.lineHeight;

        if (origin.y >= size.height) {
            return;
        }
    }
}

@end
Sulthan
la source