web-dev-qa-db-fra.com

Aligner verticalement le texte en haut dans un UILabel

J'ai un UILabel avec un espace pour deux lignes de texte. Parfois, lorsque le texte est trop court, ce texte est affiché au centre vertical de l'étiquette.

Comment aligner verticalement le texte pour toujours figurer en haut de la UILabel?

image representing a UILabel with vertically-centered text

2057
Stefan

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

Voici le moyen rapide et facile de le faire:

    [myLabel sizeToFit];

sizeToFit to squeeze a label


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

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Longer label text with sizeToFit


Version plus longue

Je vais faire mon étiquette en code afin que vous puissiez voir ce qui se passe. Vous pouvez également configurer la plupart de ces éléments dans Interface Builder. Ma configuration est une application basée sur une vue avec une image d'arrière-plan que j'ai créée dans Photoshop pour afficher les marges (20 points). L'étiquette est de 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 limitations de sizeToFit entrent 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];

enter image description here

L'étiquette est toujours de taille avec un coin fixe en haut à gauche. Vous pouvez enregistrer la largeur de l'étiquette d'origine dans une variable et la définir après sizeToFit, ou lui attribuer 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;

label alignment


Notez que sizeToFit respectera la largeur minimale de votre étiquette initiale. Si vous commencez avec une étiquette de largeur 100 et que vous appelez sizeToFit dessus, vous obtiendrez une étiquette (éventuellement très grande) de largeur 100 (ou un peu moins). Vous voudrez peut-être définir votre étiquette à la largeur minimale souhaitée avant le redimensionnement.

Correct label alignment by resizing the frame width

Quelques autres choses à noter:

Le respect de lineBreakMode dépend de la manière dont elle est définie. NSLineBreakByTruncatingTail (la valeur par défaut) est ignorée après sizeToFit, de même que les deux autres modes de troncature (tête et milieu). NSLineBreakByClipping est également ignoré. NSLineBreakByCharWrapping fonctionne comme d'habitude. La largeur du cadre est toujours réduite pour correspondre à la lettre la plus à droite.


Mark Amery a corrigé les problèmes de NIB et de Storyboards en utilisant la disposition automatique dans les commentaires:

Si votre étiquette est incluse dans une plume ou un storyboard en tant que sous-vue de la view d'un ViewController qui utilise autolayout, le fait de placer votre appel sizeToFit dans viewDidLoad ne fonctionnera pas, car la taille et le positionnement des sous-vues après viewDidLoad seront appelés et seront immédiatement annulés les effets de votre appel sizeToFit. Cependant, appeler sizeToFit à partir de viewDidLayoutSubviewssera travail.


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

Ceci utilise la méthode NSStringsizeWithFont:constrainedToSize:lineBreakMode: pour calculer la hauteur de trame nécessaire pour adapter une chaîne, puis définit l'origine et la largeur.

Redimensionnez le cadre de l'étiquette à l'aide du texte que vous souhaitez insérer. De cette façon, vous pouvez accueillir un nombre quelconque 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;
2617
nevan king
  1. Définissez le nouveau texte:

    myLabel.text = @"Some Text"
    
  2. Définissez le maximum number des lignes sur 0 (automatique):

    myLabel.numberOfLines = 0
    
  3. Définissez le cadre de l'étiquette sur la taille maximale:

    myLabel.frame = CGRectMake(20,20,200,800)
    
  4. Appelez sizeToFit pour réduire la taille du cadre afin que le contenu s'adapte parfaitement:

    [myLabel sizeToFit]
    

Le cadre des étiquettes est maintenant juste assez haut et large pour s’adapter à votre texte. Le coin supérieur gauche devrait rester inchangé. Je l'ai testé uniquement avec du texte aligné en haut à gauche. Pour d'autres alignements, vous devrez peut-être modifier le cadre ultérieurement.

De plus, l'habillage de Word est activé sur mon étiquette.

322
Jakob Egger

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 l'iPhone UILabels 'renvoie le retour en fin de chariot semble être ignoré :(

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

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

Ensuite, appelez [yourLabel alignTop]; ou [yourLabel alignBottom]; après chaque assignation de texte yourLabel.

155

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

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

 enter image description here

131
jowie

Pas de muss, pas de chichi

@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 muss, pas d'objectif-c, pas de chichi 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)
    }

}
89
jasongregori

Comme la réponse ci-dessus, mais ce n'était pas tout à fait correct, ou facile à insérer dans le code alors 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, à utiliser, placez votre texte dans l'étiquette, puis appelez la méthode appropriée pour l'aligner:

[myLabel alignTop];

ou

[myLabel alignBottom];
77
BadPirate

Un moyen encore plus rapide (et plus salissant) d'y parvenir est de définir le mode de saut de ligne de UILabel sur "Clip" et d'ajouter un nombre 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 celle-ci dépasse le nombre de lignes que vous affichez, vous devez utiliser l'une des options suivantes: les plus longs morceaux de code - mais dans de nombreux cas, cela vous donnera ce dont vous avez besoin.

67
Purple Ninja Girl

Au lieu de UILabel, vous pouvez utiliser UITextField avec l’option d’alignement vertical suivante:

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
59
ivanzoid

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

Cela vous donnera une UILabel qui réduira automatiquement le texte à 0,5 échelle et le centrera verticalement. Ces options sont également disponibles dans Storyboard/IB.

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];
48
David Greco

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

Modifier

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

#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
42

enter image description here

Dans Interface Builder

  • Définissez UILabel à la taille du plus grand texte possible
  • Définissez Lines sur '0' dans l'inspecteur d'attributs

Dans votre code

  • Définir le texte de l'étiquette
  • Appelez sizeToFit sur votre étiquette

Extrait de code:

self.myLabel.text = @"Short Title";
[self.myLabel sizeToFit];
38
bearMountain

Solution Storyboard: Le moyen le plus simple et le plus simple consiste à incorporer Label dans StackView et à définir le Axis de StackView sur Horizontal, Alignment à Top dans Attribute Inspector dans Storyboard.

 enter image description here

36
Baig

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 .

33
Martin Wickman

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

Contraintes

  1. Réglage des contraintes UILabel LefMargin, RightMargin et Top Margin.

  2. Modifiez Content Compression Resistance Priority For Vertical = 1000` de manière à ce que Vertical> Horizontal.

 Constraints of UILabel

Édité:

noOfLines=0

et les contraintes suivantes suffisent pour atteindre les résultats souhaités.

 enter image description here

32

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

 // ajuste la hauteur d'une étiquette multiligne 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 Interface, je saute quelques détails sur les 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!"; : lblHello]; 

Je l'ai également écrit sur mon blog sous la forme d'un article: http://fstoke.me/blog/?p=2819

27
firestoke

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

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

Ici, j’ai utilisé un autre moyen de le résoudre, simplement en remplissant les nouvelles lignes jusqu’à la fin du libellé (notez 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 "];
}
26
Walty Yeung

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 de sorte qu'il soit aligné en haut. Il suffit de mettre un UILabel en tant que 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
19
user486646

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;
16
Andrew Romanov

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

La mise en page automatique rend ce problème plutôt trivial. En supposant que nous ajoutions l'étiquette à UIView *view, le code suivant accomplira ceci:

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 utilisant sa intrinsicContentSize) et l'étiquette sera positionnée bord à bord horizontalement, en haut de la view.

14
petehare

Je souhaitais avoir une étiquette pouvant comporter plusieurs lignes, une taille de police minimale et centrée à la fois horizontalement et verticalement dans la vue parent. J'ai ajouté mon label par programmation à mon point de 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 ça aide quelqu'un!

14
Johnus

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 nouvelles lignes dans la chaîne fera en sorte que le texte remplisse l'espace vertical disponible, et définissez le paramètre UILabel pour tronquer tout texte en débordement.

Parce que parfois assez bon est assez bon.

14
Code Roadie

FXLabel (sur github) fait ceci en sortant de la boîte en définissant label.contentMode à UIViewContentModeTop. Ce composant n’est pas fabriqué par moi, mais c’est un composant que j’utilise fréquemment et qui possède une multitude de fonctionnalités et qui semble bien fonctionner.

13
jjxtra

si vous lisez ceci car le texte à l'intérieur de votre étiquette n'est pas centré verticalement, n'oubliez pas que certains types de police ne sont pas conçus de la même manière. Par exemple, si vous créez une étiquette avec une taille de zapfino 16, vous verrez que le texte n'est pas parfaitement centré verticalement.

cependant, travailler avec helvetica va centrer votre texte verticalement.

11
Nir Pengas

Si vous utilisez autolayout, définissez la valeur verticale contentHuggingPriority sur 1000, dans le code ou dans IB. Dans IB, vous devrez peut-être ensuite supprimer une contrainte de hauteur en définissant sa priorité sur 1, puis en la supprimant.

10
phatmann

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 avec une 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 rectangle de dessin comme ci-dessus que de manipuler numberOfLines.

P.S. 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];
}
10
jrc

Tant que vous n'effectuez aucune tâche complexe, vous pouvez utiliser UITextView au lieu de UILabels.

Désactiver le défilement.

Si vous voulez que le texte soit affiché complètement, utilisez uniquement les méthodes sizeToFit et sizeThatFits: de l'utilisateur

7
thesummersign

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)
    }
}
7
ma11hew28

Ceci est une ancienne solution, utilisez autolayout sur iOS> = 6

Ma solution:
1/Fractionner des lignes par moi-même (en ignorant les paramètres d’emballage des étiquettes)
2/Dessiner des lignes par moi-même (en ignorant l'alignement de l'étiquette)


@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
6
Sulthan

Dans UILabel verticalement, l'alignement du texte n'est pas possible. Cependant, vous pouvez modifier dynamiquement la hauteur de l'étiquette à l'aide de la méthode sizeWithFont: de NSString et définir ses x et y comme vous le souhaitez.

Vous pouvez utiliser UITextField. Il prend en charge le contentVerticalAlignment peoperty car il s’agit d’une sous-classe de UIControl. Vous devez définir sa userInteractionEnabled sur NO pour empêcher l'utilisateur de saisir du texte dessus.

6
atul awasthi

Si la création de votre propre vue personnalisée est une option, vous pouvez procéder comme suit:

- (void)drawRect:(CGRect)rect
{
    CGRect bounds = self.bounds;
    [self.textColor set];
    [self.text drawInRect:bounds
                 withFont:self.font
            lineBreakMode:UILineBreakModeTailTruncation
                alignment:self.textAlignment];
}
6
jcjimenez

Je sais que c’est un vieux message, mais l’alignement vertical du texte est un problème ÉNORME (du moins pour moi) et j’ai pensé que je devrais partager cette solution car je n’arrivais pas à en trouver un. 

L'utilisation de drawRect est un peu cher à mon avis. La bonne façon d’aligner verticalement un UILabel est de ne pas utiliser un UILabel. Utilisez une UITextView (UITextField multiligne) et observez la propriété content de la manière suivante:

- (UITextView*)textView{
     if(!_textView){
        UIEdgeInsets insets = UIEdgeInsetsMake(0, 50, 0, 5);
        CGRect frame = CGRectMake(0.0f, 0.0f, 100.0f, 100.0f);
        _textView = [[UITextView alloc]initWithFrame:UIEdgeInsetsInsetRect(frame, insets)];
        _textView.delegate = self;
        _textView.scrollEnabled = YES;
        _textView.bounces = YES;
        _textView.backgroundColor = [UIColor whiteColor];
        [_textView setUserInteractionEnabled:NO];
        [_textView addObserver:self forKeyPath:@"contentSize" options:(NSKeyValueObservingOptionNew) context:NULL];
    }
    return _textView;
}

 -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:    (NSDictionary *)change context:(void *)context {
UITextView *tv = object;

CGFloat height = [tv bounds].size.height;
CGFloat contentheight;

#ifdef __IPHONE_7_0
    contentheight = [tv sizeThatFits:CGSizeMake(tv.frame.size.width, FLT_MAX)].height;
#else
    contentheight = [tv contentSize].height;
#endif

    switch(self.verticalAlignment) {
        case VerticalAlignmentBottom:{
            CGFloat topCorrect = ([tv bounds].size.height - contentheight);
            topCorrect = (topCorrect <0.0 ? 0.0 : topCorrect);
            tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
        }
        break;
        case VerticalAlignmentMiddle:{
            CGFloat topCorrect = (height - contentheight * [tv zoomScale])/2.0;
            topCorrect = ( topCorrect < 0.0 ? 0.0 : topCorrect );
            tv.contentOffset = (CGPoint){.x = 0, .y = -topCorrect};
        }
            break;
        case VerticalAlignmentTop:{
            tv.contentOffset = (CGPoint){.x = 0, .y = 0 };
        }
            break;
        default:
            break;
    }
}

Ce qui se passe ici, c’est que nous définissons la classe dans laquelle nous sommes en tant qu’observateur, en examinant la propriété contentSize avec une option de NSKeyValueObservingOptionNew afin que chaque fois que le contenu change, -(void)observeValueForKeyPath:ofObject:change:context: soit appelé, puis vous pouvez calculer une taille de décalage pour aligner le texte de manière appropriée. 

Je ne peux pas en prendre le crédit, l'idée originale est venue de ici . Mais cette solution ne fonctionne pas sur iOS7. Après avoir traîné autour de SO pendant quelques heures, j'ai trouvé ceci: Correction d'alignement vertical iOS 7 . La ligne de clé à cet endroit est contentheight = [tv sizeThatFits:CGSizeMake(tv.frame.size.width, FLT_MAX)].height;. Pour une raison quelconque dans iOS 7, obtenir la hauteur contentSize ne fonctionne pas, mais cela résout le problème. Aucune des deux solutions ne fonctionnait seule, mais après un peu de bricolage, j'ai été capable de synthétiser ensemble la solution ci-dessus. 

6
barndog

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

5
Jack
yourLabel.baselineAdjustment = UIBaselineAdjustmentAlignCenters;
5
mbo42

Swift 2.0:

Définissez des valeurs d'énumération constantes dans un fichier Swift vide.

//  AppRef.Swift

import UIKit
import Foundation

enum UILabelTextPositions : String {

 case VERTICAL_ALIGNMENT_TOP = "VerticalAlignmentTop"
 case VERTICAL_ALIGNMENT_MIDDLE = "VerticalAlignmentMiddle"
 case VERTICAL_ALIGNMENT_BOTTOM = "VerticalAlignmentBottom"

}

Utiliser l'extension UILabel:

Créez une classe Swift vide et nommez-la. Ajouter ce qui suit

//  AppExtensions.Swift

import Foundation
import UIKit

extension UILabel{ 
 func makeLabelTextPosition (sampleLabel :UILabel?, positionIdentifier : String) -> UILabel
 {
  let rect = sampleLabel!.textRectForBounds(bounds, limitedToNumberOfLines: 0)

  switch positionIdentifier
  {
  case "VerticalAlignmentTop":
   sampleLabel!.frame = CGRectMake(bounds.Origin.x+5, bounds.Origin.y, rect.size.width, rect.size.height)
   break;

  case "VerticalAlignmentMiddle":
   sampleLabel!.frame = CGRectMake(bounds.Origin.x+5,bounds.Origin.y + (bounds.size.height - rect.size.height) / 2,
    rect.size.width, rect.size.height);
   break;

  case "VerticalAlignmentBottom":
   sampleLabel!.frame = CGRectMake(bounds.Origin.x+5, bounds.Origin.y + (bounds.size.height - rect.size.height),rect.size.width, rect.size.height);
   break;

  default:
   sampleLabel!.frame = bounds;
   break;
  }
  return sampleLabel!

 }
}

Utilisation:  

myMessageLabel.makeLabelTextPosition(messageLabel, positionIdentifier: UILabelTextPositions.VERTICAL_ALIGNMENT_TOP.rawValue)
5
A.G

Je travaillais aussi sur ce problème particulier, alors j’ai repris les idées de DS et de nevan king et les a fondamentalement combinées dans une sous-classe qui implémente une propriété d'alignement vertical, ce qui vous permet également de modifier l'alignement plusieurs fois. Il emprunte le type UIControlContentVerticalAlignment et prend également en charge UIControlContentVerticalAlignmentFill.

D'après ce que j'ai vu, numberOfLines semble inutile en ce qui concerne l'alignement vertical. Par conséquent, dans cette sous-classe, il est toujours mis à 0 lors de l'application d'un alignement vertical. En outre, vous devez toujours définir vous-même lineBreakMode si vous souhaitez une étiquette de texte multiligne.

Le voici: QALabel sur GitHub

5
vbwx

J'ai suivi la suggestion de dalewking et ajouté un UIEdgeInset pour permettre une marge ajustable. Beau travail autour.

- (id)init
{
    if (self = [super init]) {
        contentEdgeInsets = UIEdgeInsetsZero;
    }

    return self;
}

- (void)layoutSubviews
{
    CGRect localBounds = self.bounds;
    localBounds = CGRectMake(MAX(0, localBounds.Origin.x + contentEdgeInsets.left), 
                             MAX(0, localBounds.Origin.y + contentEdgeInsets.top), 
                             MIN(localBounds.size.width, localBounds.size.width - (contentEdgeInsets.left + contentEdgeInsets.right)), 
                             MIN(localBounds.size.height, localBounds.size.height - (contentEdgeInsets.top + contentEdgeInsets.bottom)));

    for (UIView *subview in self.subviews) {
        if ([subview isKindOfClass:[UILabel class]]) {
            UILabel *label = (UILabel*)subview;
            CGSize lineSize = [label.text sizeWithFont:label.font];
            CGSize sizeForText = [label.text sizeWithFont:label.font constrainedToSize:localBounds.size lineBreakMode:label.lineBreakMode];

            NSInteger numberOfLines = ceilf(sizeForText.height/lineSize.height);

            label.numberOfLines = numberOfLines;
            label.frame = CGRectMake(MAX(0, contentEdgeInsets.left), MAX(0, contentEdgeInsets.top), localBounds.size.width, MIN(localBounds.size.height, lineSize.height * numberOfLines)); 
        }
    }
}
5
sean woodward

Pour Swift 3 ...

@IBDesignable class TopAlignedLabel: UILabel {
    override func drawText(in rect: CGRect) {
        if let stringText = text {
            let stringTextAsNSString = stringText as NSString
            let labelStringSize = stringTextAsNSString.boundingRect(with: CGSize(width: self.frame.width,height: CGFloat.greatestFiniteMagnitude),
                                                                            options: NSStringDrawingOptions.usesLineFragmentOrigin,
                                                                            attributes: [NSFontAttributeName: font],
                                                                            context: nil).size
            super.drawText(in: CGRect(x:0,y: 0,width: self.frame.width, height:ceil(labelStringSize.height)))
        } else {
            super.drawText(in: rect)
        }
    }
    override func prepareForInterfaceBuilder() {
        super.prepareForInterfaceBuilder()
        layer.borderWidth = 1
        layer.borderColor = UIColor.black.cgColor
    }
}
4
seggy

Il y a deux façons de résoudre ce problème.

[mylabel setNumberOfLines:0];
[mylabel sizeToFit];

Mais le deuxième moyen est plus fiable pour cette approche, c’est-à-dire

 CGSize sizeToFit = [label.text sizeWithFont:label.font constrainedToSize:maxSize lineBreakMode:label.lineBreakMode];
 [mylabel setFrame:CGRectMake(mylabel.frame.Origin.x, mylabel.frame.Origin.y, sizeToFit.width, sizeToFit.height)];

entrer "\ n" n’est pas une bonne chose, mais oui, si vous connaissez les contraintes et la taille des données à afficher, cela peut fonctionner mais vous ne pouvez pas le développer si le texte est plus long que la taille de l’étiquette. La deuxième façon de définir le cadre en fonction de la taille du texte à afficher.

4
SunnyChattha

Ce code vous aide à aligner le texte en haut et à fixer la hauteur de l’étiquette au contenu du texte.

instruction d'utiliser le code ci-dessous

isHeightToChange par défaut, il est vrai que la hauteur de l'étiquette est identique à celle du contenu du texte automatiquement .isHeightToChange si elle est fausse, le texte est aligné en haut sans diminution de hauteur.

#import "DynamicHeightLable.h"

@implementation DynamicHeightLable
@synthesize isHeightToChange;
- (id)initWithFrame:(CGRect)frame 
{
    self = [super initWithFrame:frame];
    if (self)
    {
        // Initialization code.
        self.isHeightToChange=FALSE;//default

    }
    return self;
}
- (void)drawTextInRect:(CGRect)rect
{
    if(!isHeightToChange){
    CGSize maximumLabelSize = CGSizeMake(self.frame.size.width,2500);

    CGFloat height = [self.text sizeWithFont:self.font
                           constrainedToSize:maximumLabelSize
                               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];

}
- (void) layoutSubviews
{
    [super layoutSubviews];
       if(isHeightToChange){
     CGRect ltempFrame = self.frame;
    CGSize maximumLabelSize = CGSizeMake(self.frame.size.width,2500);

    CGFloat height = [self.text sizeWithFont:self.font
                           constrainedToSize:maximumLabelSize
                               lineBreakMode:self.lineBreakMode].height;
    if (self.numberOfLines != 0) {
        height = MIN(height, self.font.lineHeight * self.numberOfLines);
    }
    ltempFrame.size.height = MIN(ltempFrame.size.height, height);

    ltempFrame.size.height=ltempFrame.size.height;
    self.frame=ltempFrame;
       }
}
@end
3
Ramesh Muthe

Pour ceux d'entre vous dont les cellules de tableau personnalisées tentent de résoudre ce problème, ajoutez ceci à votre classe de cellules de tableau personnalisée:

Swift 2.2:

override func layoutSubviews() {
    labelName.sizeToFit()
}

Cela a résolu mon problème.

3
Elliott Davies

(Au 7 mars 2018)

Swift 4

let maxFrameHeight = 75

myLabel = UILabel()
myLabel.frame = CGRect(x: 9, y: 9, width: 126, height: maxFrameHeight)
myLabel.text = "my labels text displayed"
myLabel.numberOfLines = 0

myLabel.sizeToFit()
let newFrameHeight = myLabel.frame.size.height
let safeNewHeight = min(newFrameHeight, maxFrameHeight)

myLabel.frame = CGRect(x: 9, y: 9, width: 126, height: safeNewHeight)

Cela donnera le résultat souhaité et garantira que la nouvelle hauteur de UILabel ne dépasse pas la hauteur maximale souhaitée pour cette étiquette.

3
BennyTheNerd

Au lieu d'utiliser sizeToFit, vous pouvez modifier manuellement la hauteur de l'étiquette.

CGSize size = [descLabel.text sizeWithFont:descLabel.font constrainedToSize:CGSizeMake(labelWidth, labelHeight)];
CGRect frame = descLabel.frame;
frame.size.height = size.height;    
[yourLabel setFrame:frame];

La taille retournée correspondra le mieux au contenu de votre étiquette. Si la hauteur de l'étiquette est adaptée à son contenu, vous ne rencontrerez aucun problème de positionnement du contenu au centre de l'étiquette. 

3
KarenAnne

En me basant sur toutes les autres solutions publiées, j'ai créé une petite sous-classe UILabel simple qui gérera l'alignement vertical pour vous lors de la définition de sa propriété d'alignement. Cela mettra également à jour l'étiquette lors des changements d'orientation, contraindra la hauteur au texte et maintiendra la largeur de l'étiquette à sa taille d'origine.

.h

@interface VAlignLabel : UILabel
@property (nonatomic, assign) WBZVerticalAlignment alignment;
@end


.m

 -(void)setAlignment:(WBZVerticalAlignment)alignment{

    _alignment = alignment;

    CGSize s = [self.text sizeWithFont:self.font constrainedToSize:CGSizeMake(self.frame.size.width, 9999) lineBreakMode:NSLineBreakByWordWrapping];

    switch (_alignment)
    {
        case wbzLabelAlignmentVerticallyTop:
            self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y, self.frame.size.width, s.height);
            break;
        case wbzLabelAlignmentVerticallyMiddle:
            self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y + (self.frame.size.height - s.height)/2, self.frame.size.width, s.height);
            break;
        case wbzLabelAlignmentVerticallyBottom:
            self.frame = CGRectMake(self.frame.Origin.x, self.frame.Origin.y + (self.frame.size.height - s.height), self.frame.size.width, s.height);
            break;
        default:
            break;
    }
}

-(void)layoutSubviews{
    [self setAlignment:self.alignment];
}
2
cohen72

Solution simple à ce problème ... 

Placez une UIStackView au-dessous de l'étiquette. 

Utilisez ensuite AutoLayout pour définir un espacement vertical de zéro sur l'étiquette et contraindre le haut à ce qui était précédemment le bas de l'étiquette. Le label ne va pas grandir, le stackView va le faire. Ce qui me plaît dans cette situation est que la vue de la pile n’est pas un rendu. Donc, ce n'est pas vraiment perdre du temps à rendre. Même s'il effectue des calculs AutoLayout.

Vous aurez probablement besoin de jouer avec ContentHugging and Resistance, mais c’est aussi simple. 

De temps en temps, je recherche ce problème sur Google et je reviens ici. Cette fois, j'ai eu une idée que je pense être la plus facile, et je ne pense pas que ce soit une mauvaise performance. Je dois dire que je viens d'utiliser AutoLayout et que je ne veux pas vraiment me soucier de calculer des images. C'est juste ... beurk.

1
Nuno Gonçalves

Cela peut être fait avec plus de flexibilité en définissant un height constraint sur l'étiquette dans Interface Builder, en le liant au code avec un IBOutlet et en modifiant cette hauteur pour afficher le texte dans une position verticale concrète. Exemple d'alignement central et inférieur:

labelHeightConstraint.constant = centerAlignment ? 30 : 15
layoutIfNeeded()
0
Ruben Marin

Voici une solution dans Swift 4.0:

func viewDidLoad() {

    super.viewDidLoad()

    // 20 point top and left margin. Sized to leave 20 pt at right.
    let labelFrame = CGRect(x: 20, y: 20, width: 280, height: 150)
    let myLabel = UILabel(frame: labelFrame)
    myLabel.backgroundColor = UIColor.orange

    let labelText = "I am the very model of a modern Major-General, 
    I've information vegetable, animal, and mineral"
    myLabel.text = labelText

    // Tell the label to use an unlimited number of lines
    myLabel.numberOfLines = 0
    myLabel.sizeToFit()

    view.addSubview(myLabel) // add label
}
0
Ashish