Pregunta Alinear verticalmente el texto hacia arriba dentro de un UILabel


tengo un UILabel con espacio para dos líneas de texto. A veces, cuando el texto es demasiado corto, este texto se muestra en el centro vertical de la etiqueta.

¿Cómo alinear verticalmente el texto para estar siempre en la parte superior de la UILabel?

image representing a UILabel with vertically-centered text


1984
2018-06-28 08:54


origen


Respuestas:


No hay forma de configurar la alineación vertical en una UILabel, pero puede obtener el mismo efecto cambiando el marco de la etiqueta. He hecho que mis etiquetas sean naranjas para que puedas ver claramente lo que está sucediendo.

Aquí está la manera rápida y fácil de hacer esto:

    [myLabel sizeToFit];

sizeToFit to squeeze a label


Si tiene una etiqueta con texto más largo que formará más de una línea, configure numberOfLines a 0 (cero aquí significa un número ilimitado de líneas).

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Longer label text with sizeToFit


Versión más larga

Haré mi etiqueta en código para que pueda ver lo que está pasando. Puede configurar la mayor parte de esto en Interface Builder también. Mi configuración es una aplicación basada en la vista con una imagen de fondo que hice en Photoshop para mostrar los márgenes (20 puntos). La etiqueta es de un color naranja atractivo para que pueda ver lo que está sucediendo con las dimensiones.

- (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];
}

Algunas limitaciones de uso sizeToFit entre en juego con el texto alineado al centro o a la derecha. Esto es lo que sucede:

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

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

enter image description here

La etiqueta todavía se clasifica con una esquina superior izquierda. Puede guardar el ancho de la etiqueta original en una variable y configurarlo después sizeToFit, o darle un ancho fijo para contrarrestar estos problemas:

    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


Tenga en cuenta que sizeToFit respetará el ancho mínimo de su etiqueta inicial. Si comienza con una etiqueta de 100 de ancho y llame sizeToFit en él, le devolverá una etiqueta (posiblemente muy alta) con un ancho de 100 (o un poco menor). Es posible que desee establecer su etiqueta con el ancho mínimo que desee antes de cambiar el tamaño.

Correct label alignment by resizing the frame width

Algunas otras cosas a tener en cuenta:

Si lineBreakMode se respeta depende de cómo se establezca. NSLineBreakByTruncatingTail (el valor predeterminado) se ignora después sizeToFit, al igual que los otros dos modos de truncamiento (cabeza y medio). NSLineBreakByClipping también es ignorado NSLineBreakByCharWrapping funciona como de costumbre El ancho del marco aún se reduce para ajustarse a la letra de la derecha.


Mark Amery dio una solución para NIBs y Storyboards usando Auto Layout en los comentarios:

Si su etiqueta está incluida en una punta o guión gráfico como una subvista de la view de un ViewController que usa el autolayout, luego colocando su sizeToFit llamar a viewDidLoad no funcionará, ya que el autodiseño dimensiona y posiciona las subvistas después viewDidLoad se llama y deshará inmediatamente los efectos de su sizeToFit llamada. Sin embargo, llamando sizeToFit desde adentro viewDidLayoutSubviews  será trabajo.


Mi respuesta original (para posteridad / referencia):

Esto usa el NSString método sizeWithFont:constrainedToSize:lineBreakMode: para calcular la altura del cuadro necesaria para caber una cuerda, luego establece el origen y el ancho.

Cambiar el tamaño del marco de la etiqueta con el texto que desea insertar. De esta forma puede acomodar cualquier cantidad de líneas.

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;

2561
2018-06-28 10:36



  1. Establecer el nuevo texto:

    myLabel.text = @"Some Text"
    
  2. Selecciona el maximum number de líneas a 0 (automático):

    myLabel.numberOfLines = 0
    
  3. Establezca el marco de la etiqueta al tamaño máximo:

    myLabel.frame = CGRectMake(20,20,200,800)
    
  4. Llamada sizeToFit para reducir el tamaño del marco para que el contenido se ajuste:

    [myLabel sizeToFit]
    

El marco de etiquetas ahora es alto y lo suficientemente ancho como para adaptarse a su texto. La parte superior izquierda no debe ser modificada. Lo he probado solo con el texto alineado arriba a la izquierda. Para otras alineaciones, puede que tenga que modificar el marco después.

Además, mi etiqueta tiene activado el ajuste de palabras.


315
2017-08-27 16:24



Refiriéndose a la solución de extensión:

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

debe ser reemplazado por

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

Se necesita espacio adicional en cada nueva línea agregada, porque el iPhone UILabels'los retornos del carro al final parecen ser ignorados :(

Del mismo modo, alignBottom se debe actualizar también con un @" \n@%" en lugar de "\n@%" (para la inicialización del ciclo debe reemplazarse por "for (int i = 0 ..." también).

La siguiente extensión funciona para mí:

// -- 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

Luego llame [yourLabel alignTop]; o [yourLabel alignBottom]; después de cada asignación de texto de su etiqueta.


151
2017-09-09 13:01



En caso de que sea de alguna ayuda para alguien, tuve el mismo problema pero pude resolver el problema simplemente cambiando de usar UILabel para usar UITextView. Aprecio que esto no sea para todos porque la funcionalidad es un poco diferente.

Si cambias a usar UITextView, puede desactivar todas las propiedades de la Vista de desplazamiento así como la Interacción del usuario habilitada ... Esto lo forzará a actuar más como una etiqueta.

enter image description here


126
2017-10-05 12:46



Sin despeinarse sin problemas

@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

No muss, no Objective-c, sin complicaciones, pero 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)
    }

}

80
2018-05-21 07:47



Me gusta la respuesta anterior, pero no estaba del todo bien, o es fácil copiar el código, así que lo limpié un poco. Agregue esta extensión a su propio archivo .h y .m o simplemente péguelo justo arriba de la implementación que pretende usar:

#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

Y luego para usar, coloque su texto en la etiqueta y luego llame al método apropiado para alinearlo:

[myLabel alignTop];

o

[myLabel alignBottom];

76
2018-03-04 02:44



Una forma aún más rápida (y más sucia) de lograr esto es establecer el modo de corte de línea de UILabel en "Clip" y agregar una cantidad fija de líneas nuevas.

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

Esta solución no funcionará para todos, en particular, si aún desea mostrar "..." al final de la cadena si supera el número de líneas que muestra, deberá usar una de los bits de código más largos, pero en muchos casos esto te dará lo que necesitas.


66
2018-04-18 21:08



En lugar de UILabel puedes utilizar UITextField que tiene una opción de alineación vertical:

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

56
2018-02-10 10:36



He luchado con este durante mucho tiempo y quería compartir mi solución.

Esto te dará una UILabel que reescribirá automáticamente el texto hasta 0.5 escalas y centrará verticalmente el texto. Estas opciones también están disponibles en Storyboard / IB.

[labelObject setMinimumScaleFactor:0.5];
[labelObject setBaselineAdjustment:UIBaselineAdjustmentAlignCenters];

47
2017-10-16 16:38



Crea una nueva clase

LabelTopAlign

archivo .h

#import <UIKit/UIKit.h>


@interface KwLabelTopAlign : UILabel {

}

@end

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

Editar

Aquí hay una implementación más simple que hace lo mismo:

#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
2017-08-04 08:07