Alinee verticalmente el texto hacia arriba dentro de un UILabel

2175

Tengo un UILabelespacio 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 alineo verticalmente el texto para que siempre esté en la parte superior de la UILabel?

imagen que representa un UILabel con texto centrado verticalmente

Stefan
fuente

Respuestas:

2702

No hay forma de establecer la alineación vertical en un UILabel, pero puede obtener el mismo efecto cambiando el marco de la etiqueta. He hecho mis etiquetas de color naranja para que pueda ver claramente lo que está sucediendo.

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

    [myLabel sizeToFit];

sizeToFit para exprimir una etiqueta


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

    myLabel.numberOfLines = 0;
    [myLabel sizeToFit];

Texto de etiqueta más largo con sizeToFit


Versión más larga

Haré mi etiqueta en el código para que pueda ver lo que está sucediendo. También puede configurar la mayor parte de esto en Interface Builder. 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 atractivo color naranja para que pueda ver qué sucede 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 sizeToFitentran en juego con texto alineado al centro o a la derecha. Esto es lo que pasa:

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

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

ingrese la descripción de la imagen aquí

La etiqueta todavía tiene el tamaño con una esquina superior izquierda fija. Puede guardar el ancho de la etiqueta original en una variable y configurarlo después sizeToFit, o asignarle 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;

alineación de etiquetas


Tenga en cuenta que sizeToFitrespetará el ancho mínimo de su etiqueta inicial. Si comienza con una etiqueta de 100 de ancho y la llama sizeToFit, le devolverá una etiqueta (posiblemente muy alta) con 100 (o un poco menos) de ancho. Es posible que desee establecer su etiqueta al ancho mínimo que desea antes de cambiar el tamaño.

Corrija la alineación de la etiqueta cambiando el tamaño del ancho del marco

Algunas otras cosas a tener en cuenta:

Si lineBreakModese respeta depende de cómo se establece. NSLineBreakByTruncatingTail(el valor predeterminado) se ignora después sizeToFit, al igual que los otros dos modos de truncamiento (head y middle). NSLineBreakByClippingTambién se ignora. NSLineBreakByCharWrappingFunciona como siempre. El ancho del marco todavía se reduce para ajustarse a la letra más a la derecha.


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

Si su etiqueta se incluye en una plumilla o guión gráfico como una subvista de viewun ViewController que utiliza la distribución automática, entonces poner su sizeToFitllamada en viewDidLoadno funcionará, porque el tamaño de la distribución automática y las posiciones de las subvistas después viewDidLoadse llama e inmediatamente deshacerá los efectos de su sizeToFitllamada. Sin embargo, llamando sizeToFitdesde dentro de viewDidLayoutSubviews la voluntad de trabajo.


Mi respuesta original (para posteridad / referencia):

Utiliza el NSStringmétodo sizeWithFont:constrainedToSize:lineBreakMode:para calcular la altura del marco necesaria para ajustar una cadena, luego establece el origen y el ancho.

Cambie el tamaño del marco para la etiqueta usando el texto que desea insertar. De esa manera puede acomodar cualquier número 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;
nevan king
fuente
10
debe eliminar la distribución automática
hungbm06
44
Aquí hay una manera simple de resolver el problema: stackoverflow.com/a/26632490/636559
AboulEine en
Si usa el diseño automático y el guión gráfico, será útil agregar una restricción con la opción "Eliminar en el momento de la compilación"
JK ABC
Si necesita que esto funcione adjustsFontSizeToFitWidth, use sizeToFit y luego configure el marco de la etiqueta en 0, 0, theWidthYouWant, label.frame.size.height (que es la nueva altura determinada por sizeToFit) LUEGO apliqueadjustsFontSizeToFitWidth
Albert Renshaw
3
Esta respuesta es innecesariamente complicada y está usando una "palabra alrededor". Consulte la sugerencia a continuación de simplemente cambiar la prioridad de abrazo de contenido vertical. No requiere ningún código ...
Beetree
335
  1. Establecer el nuevo texto:

    myLabel.text = @"Some Text"
  2. Establezca las maximum numberlíneas en 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. Llame sizeToFitpara reducir el tamaño del marco para que el contenido se ajuste:

    [myLabel sizeToFit]

El marco de las etiquetas ahora es lo suficientemente alto y ancho como para adaptarse a su texto. La parte superior izquierda no debe modificarse. He probado esto solo con el texto alineado a la izquierda superior. Para otras alineaciones, es posible que deba modificar el marco después.

Además, mi etiqueta tiene habilitado el ajuste de texto.

Jakob Egger
fuente
Hola Ben, acabo de ver mi antiguo código nuevamente y actualicé mi respuesta con los pasos correctos necesarios.
Jakob Egger
Esto funcionó bien para mi. Configuré las dimensiones de mi UILabel y cambié numberOfLines a 0 en IB y luego después de configurar el texto llamado [myLabel sizeToFit].
Dan Ellis
funciona perfectamente para mí, después de establecer el número de líneas en Attr. Inspector
d2burke
No funciona en caso de número de líneas más de 1
Tom
No funciona cuando desea una etiqueta de dos líneas, pero el texto requiere más líneas.
Jose Manuel Abarca Rodríguez
159

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 UILabelsparece que se ignora el retorno del carro posterior del iPhone :(

Del mismo modo, alignBottom también debe actualizarse 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 yourLabel.

Dmitri Sologoubenko
fuente
@DS Noté que estás agregando VerticalAlignentre paréntesis después @implementation UILabel. Al ser nuevo en Objective-C, no me he encontrado con esta sintaxis antes. ¿Como se llama esto?
Juliano
Increíble, no sabía sobre categorías, esto me da aún más aprecio por el lenguaje Objective-c. Aprender más idiomas es muy importante para convertirse en un buen programador.
JeroenEijkhof
Dio un voto a favor. pero iirc esto no funcionará si no conoce la cantidad de líneas que es una desventaja
Tjirp
Tengo que mostrar 3 líneas de texto y alinear el texto hacia arriba. Entonces, ¿debo establecer el número de líneas en 3. Cuando lo configuro en 3, estoy viendo "..." si el texto es menor (que cabe en una línea). Cómo evitar este "..."
Satyam
1
Publiqué una edición de esta categoría, espero que sea aprobada en breve. Los métodos sizeWithFont:constrainedToSize:lineBreakMode:y sizeWithFont: se deprecian en iOS7. Además, esta categoría solo funciona en etiquetas cuando numberOfLines es mayor que 0.
timgcarlson
146

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

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

ingrese la descripción de la imagen aquí

jowie
fuente
1
No estoy seguro de cómo esto afectará el rendimiento si muchos UILabels se convierten en vistas de texto.
ninjaneer
2
Todas las otras soluciones en este hilo no funcionaron para mí en iOS 7 (por alguna razón). Pero simplemente usando a UITextViewme dio el resultado deseado de inmediato.
Clifton Labrum
1
Es una solución alternativa, todavía necesita algunos ajustes para que parezca auténtico, pero de hecho, esta es la solución más fácil sin código que vi, aprobado.
Itai Spector
1
Me gusta esta solución De acuerdo, puede haber problemas de rendimiento y tal, para una etiqueta simple en una vista relativamente simple, esto fue perfecto para lo que necesitaba (y no puedo notar ningún impacto en el rendimiento)
JCutting8
1
Un pequeño inconveniente de este método es que introducirá un relleno intrínseco adicional al texto. Una solución rápida es introducir una restricción negativa.
Sira Lam
122

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

Sin muss, sin Objective-c, sin problemas, 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)
    }

}

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
fuente
¡La versión rápida funcionó para mí sin ningún efecto secundario! ¡Buen trabajo!
l.vasilev
2
Probablemente la mejor respuesta aquí, funcione en todos los escenarios. sizeToFit no funciona si la etiqueta está en UITableviewCell. Buen trabajo.
rajat chauhan
79

Me gusta la respuesta anterior, pero no estaba del todo bien, o fue fácil introducir 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 encima de la implementación que desea 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 el texto en la etiqueta y luego llame al método apropiado para alinearlo:

[myLabel alignTop];

o

[myLabel alignBottom];
BadPirate
fuente
sizeWithFont en desuso
tdios
68

Una forma aún más rápida (y más sucia) de lograr esto es estableciendo el modo de salto de línea de UILabel en "Clip" y agregando 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 su cadena si excede el número de líneas que está mostrando, deberá usar una de los bits más largos de código, pero para muchos casos esto le dará lo que necesita.

Chica Ninja Púrpura
fuente
3
Establecer el modo de salto de línea en 'clip' parece estropear el tamaño automático de la etiqueta. Usar en su UILineBreakModeWordWraplugar.
Niels van der Rest
y mejor agregue espacios "\ n \ n"
djdance
68

Enfoque más fácil usando Storyboard:

Incrustar la etiqueta en StackView y conjunto tras dos atributos de StackView en Atributo Inspector:

1- Axisa Horizontal,

2- AlignmentaTop

ingrese la descripción de la imagen aquí

Baig
fuente
1
Para obtener mejores resultados, puede hacer esto StackView> Ver> UILabel, porque cuando incrusta un UILabel dentro de un StackView, el StackView cambia el tamaño para que UILabel se ajuste al tamaño mínimo
Daniel Beltrami
Gran idea colocar UILabelen alguna UIViewsubclase ( UIViewnormalmente es simple ) y dejar que esa etiqueta crezca hacia abajo. ¡Gracias!
Viktor Kucera
1
¡Increíble! También vale la pena mencionar que es necesario limitaciones claras sobre el UILabely dejar que el StackView haga su trabajo. ¡Gracias!
Luiz Dias
Con el comando de menú Editor-> Incrustar con su etiqueta seleccionada, Xcode elimina las restricciones de las etiquetas, entonces puede establecer las restricciones para StackView. Este enfoque es más cercano a un enfoque de diseño 'SwiftUI', que es como lo descubrí.
Rocket Garden
¿Por qué funciona esto?
Novellizator
60

En lugar de UILabelusted, puede usar el UITextFieldque tiene la opción de alineación vertical:

textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
textField.userInteractionEnabled = NO; // Don't allow interaction
ivanzoide
fuente
77
Advertencia: UITextField solo permite una sola línea de texto.
David James
2
Para completar el comentario de @DavidJames: UITextView sería más apropiado
CedricSoubrie
49

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

Esto le dará una UILabelreducción automática del texto a 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];
David Greco
fuente
Funcionó como perfecto entre todas las respuestas proporcionadas. Gracias @David Greco. Junto con estas propiedades, si establecemos ajustatsFontSizeToFitWidth en YES, el texto se ajustará al marco sin modificar el marco de la etiqueta.
Ashok
43

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
sebastian friedrich
fuente
+1 para la respuesta real. A diferencia de las sizeToFitsoluciones de esta realidad hace que el UILabelponer cualquier texto en la parte superior, incluso si cambia dinámicamente un texto corto con uno más largo o viceversa.
SnakE
38

ingrese la descripción de la imagen aquí

En Interface Builder

  • Establecer el UILabeltamaño del texto más grande posible
  • Establecer Linesen '0' en el Inspector de atributos

En su código

  • Establecer el texto de la etiqueta
  • Llame sizeToFita su etiqueta

Fragmento de código:

self.myLabel.text = @"Short Title";
[self.myLabel sizeToFit];
montaña del oso
fuente
funcionó muy bien, excepto que en mi caso necesitaba establecer líneas en 2: tener un UILabel dentro de UITableViewCell y establecerlo en 0 hizo que se superpusiera, pero configurarlo en 2 funcionó (la celda tiene suficiente espacio para 2 líneas)
eselk
34

Para la IU adaptativa (iOS8 o posterior), la Alineación vertical de UILabel se configurará desde StoryBoard cambiando las propiedades noOfLines= 0` y

Restricciones

  1. Ajuste de las restricciones UILabel LefMargin, RightMargin y Top Margin.

  2. Cambio Content Compression Resistance Priority For Vertical= 1000` Para que Vertical> Horizontal.

Restricciones de UILabel

Editado:

noOfLines=0

y las siguientes restricciones son suficientes para lograr los resultados deseados.

ingrese la descripción de la imagen aquí

Rabindra Nath Nandi
fuente
Esto fue asombroso. ¿Puedes explicar por qué Vertical> Horizontal hizo el trabajo? Gracias.
Gabriel
33

Crea una subclase de UILabel. Funciona de maravilla:

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

Como se discutió aquí .

Martin Wickman
fuente
27

Escribí una función util para lograr este propósito. Puedes echar un vistazo:

// ajusta la altura de una etiqueta de varias líneas para alinearla verticalmente con la parte superior
+ (vacío) alignLabelWithTop: etiqueta (UILabel *) {
  CGSize maxSize = CGSizeMake (label.frame.size.width, 999);
  label.adjustsFontSizeToFitWidth = NO;

  // obtener la altura real
  CGSize actualSize = [label.text sizeWithFont: label.font restrictinedToSize: maxSize lineBreakMode: label.lineBreakMode];
  CGRect rect = label.frame;
  rect.size.height = actualSize.height;
  label.frame = rect;
}

.¿Cómo utilizar? (Si lblHello es creado por el generador de interfaces, entonces omito algunos detalles de los atributos de UILabel)

lblHello.text = @ "¡Hola mundo! ¡Hola mundo! ¡Hola mundo! ¡Hola mundo! ¡Hola mundo! ¡Hola mundo! ¡Hola mundo! ¡Hola mundo!";
lblHello.numberOfLines = 5;
[Utilidades alignLabelWithTop: lblHello];

También lo escribí en mi blog como un artículo: http://fstoke.me/blog/?p=2819

fuego de fuego
fuente
27

Me tomó un tiempo leer el código, así como el código en la página introducida, y descubrí que todos intentan modificar el tamaño del marco de la etiqueta, para que no aparezca la alineación vertical central predeterminada.

Sin embargo, en algunos casos queremos que la etiqueta ocupe todos esos espacios, incluso si la etiqueta tiene mucho texto (por ejemplo, varias filas con la misma altura).

Aquí, utilicé una forma alternativa de resolverlo, simplemente rellenando las nuevas líneas hasta el final de la etiqueta (tenga en cuenta que en realidad heredé el UILabel, pero no es necesario):

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
fuente
22

Lo que hice en mi aplicación fue establecer la propiedad de línea de UILabel en 0, así como crear una restricción inferior de UILabel y asegurarme de que se establezca en> = 0 como se muestra en la imagen a continuación. ingrese la descripción de la imagen aquí

Jack Tiong
fuente
19

Tomé las sugerencias aquí y creé una vista que puede envolver un UILabel y lo dimensionará y establecerá el número de líneas para que quede alineado en la parte superior. Simplemente ponga un UILabel como una subvista:

@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

fuente
19

Puede usar TTTAttributedLabel , es compatible con la alineación vertical.

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

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

He encontrado que las respuestas a esta pregunta ahora están un poco desactualizadas, por lo que agrego esto para los fanáticos del diseño automático.

El diseño automático hace que este problema sea bastante trivial. Suponiendo que agreguemos la etiqueta UIView *view, el siguiente código logrará esto:

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 altura de la etiqueta se calculará automáticamente (usando su intrinsicContentSize) y la etiqueta se colocará de borde a borde horizontalmente, en la parte superior de la etiqueta view.

petehare
fuente
15

He usado muchos de los métodos anteriores, y solo quiero agregar un enfoque rápido y sucio que he usado:

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

Asegúrese de que el número de líneas nuevas en la cadena hará que cualquier texto llene el espacio vertical disponible y configure UILabel para truncar cualquier texto que se desborde.

Porque a veces lo suficientemente bueno es lo suficientemente bueno .

Código Roadie
fuente
Sin embargo, dada la variación en las alturas del tamaño de la pantalla de los dispositivos iOS, entonces sería necesario un número variable de '\ n' para tener en cuenta la altura de cambio de uilabel (que es una posibilidad). Por lo tanto, este método no es particularmente útil a largo plazo.
Rambatino
Nota: "rápido y sucio" no es "la solución perfecta para todos los tiempos". Sólo digo'.
Código Roadie
2
@CodeRoadie Rápido y sucio hizo el truco para mí, tuve un par de problemas de diseño con la respuesta real. Podría hacerlo "de la manera correcta", ¡pero gracias por ahorrarme algo de tiempo!
Séraphin Hochart
@dGambit tenga en cuenta: "rápido y sucio "
Código Roadie
1
Algo que aprendí recientemente al cargar vistas dinámicamente UITextViewes que puede llamar dlopenpara admitir la entrada de texto. Esto puede causar un gran retraso en el hilo de la interfaz de usuario, por lo que este enfoque es mucho más eficaz.
yano
14

Quería tener una etiqueta que pudiera tener líneas múltiples, un tamaño de fuente mínimo y centrada tanto horizontal como verticalmente en su vista principal. Agregué mi etiqueta mediante programación a mi vista:

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

Y luego, cuando quería cambiar el texto de mi etiqueta ...

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

Espero que ayude a alguien!

Johnus
fuente
14

FXLabel (en github) hace esto fuera de la caja estableciendo label.contentModeen UIViewContentModeTop. Este componente no está hecho por mí, pero es un componente que uso con frecuencia y tiene toneladas de características, y parece funcionar bien.

jjxtra
fuente
11

Para cualquiera que lea esto porque el texto dentro de su etiqueta no está centrado verticalmente, tenga en cuenta que algunos tipos de fuente no están diseñados de la misma manera. por ejemplo, si crea una etiqueta con zapfino tamaño 16, verá que el texto no está perfectamente centrado verticalmente.

sin embargo, trabajar con helvetica centrará verticalmente su texto.

Nir Pengas
fuente
Muchas fuentes personalizadas no están alineadas en el centro vertical. UILabel o UITextView siempre están alineados en el centro vertical. No es necesario pensar en la alineación vertical en la programación de iOS.
Amit Singh
11

Uso 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
fuente
Usted señor es un dios!
Diogo Antunes
1
Esta respuesta debe llegar manera más upvotes ya que es de lejos la solución más limpia y mejor!
Michael Ochs
10

Subclase UILabel y restrinja el rectángulo de dibujo, así:

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

    [super drawTextInRect:rect];
}

Probé la solución que implica el relleno de nueva línea y encontré un comportamiento incorrecto en algunos casos. En mi experiencia, es más fácil restringir el dibujo correcto que el anterior que meterse con él numberOfLines.

PD: puede imaginarse fácilmente apoyando UIViewContentMode de esta manera:

- (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
fuente
10

Si está utilizando la distribución automática, establezca la ContentHuggingPriority vertical en 1000, ya sea en código o IB. En IB, puede que tenga que eliminar una restricción de altura estableciendo su prioridad en 1 y luego eliminándola.

phatmann
fuente
8

Mientras no esté haciendo ninguna tarea compleja, puede usarla en UITextViewlugar de UILabels.

Desactiva el desplazamiento.

Si desea que el texto se muestre completamente solo usuario sizeToFity sizeThatFits:métodos

thesummersign
fuente
8

En rápido,

let myLabel : UILabel!

Para que el texto de su etiqueta se ajuste a la pantalla y esté en la parte superior

myLabel.sizeToFit()

Para que su fuente de etiqueta se ajuste al ancho de la pantalla o al tamaño de ancho específico.

myLabel.adjustsFontSizeToFitWidth = YES

y algo de texto Alineación para la etiqueta:

myLabel.textAlignment = .center

myLabel.textAlignment = .left

myLabel.textAlignment = .right

myLabel.textAlignment = .Natural

myLabel.textAlignment = .Justified

Dara Tith
fuente
Solo un cambio de sintaxis de la sintaxis anterior de [myLabel sizeToFit]. ¡Gracias!
velkoon
7

Esta es una solución antigua, use el autolayout en iOS> = 6

Mi solución:

  1. Dividir líneas por mí mismo (ignorando la configuración de ajuste de etiqueta)
  2. Dibujar líneas por mí mismo (ignorando la alineación de la etiqueta)

@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
fuente