¿Cómo se agrega texto de varias líneas a un UIButton?

368

Tengo el siguiente código...

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

... la idea es que puedo tener texto de varias líneas para el botón, pero el texto siempre está oscurecido por la imagen de fondo del botón UIB. Una llamada de registro para mostrar las subvistas del botón muestra que se ha agregado el UILabel, pero el texto en sí no se puede ver. ¿Es esto un error en UIButton o estoy haciendo algo mal?

Owain Hunt
fuente
77
button.titleLabel?.numberOfLines = 0
ma11hew28
2
Tenga en cuenta que para este control de calidad muy antiguo, en Xcode moderno MUY SIMPLEMENTE, ELIJA "TEXTO ATRIBUIDO" y luego es trivial, seleccione "ajuste de caracteres".
Fattie
También vea la respuesta actualizada a una pregunta similar.
ToolmakerSteve
vea esta respuesta (varias líneas, ancho de ajuste, altura de ajuste) para el texto del botón stackoverflow.com/a/59211399/7523163
Hamid Reza Ansari

Respuestas:

676

Para iOS 6 y superior, use lo siguiente para permitir varias líneas:

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

Para iOS 5 y versiones inferiores, use lo siguiente para permitir varias líneas:

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

2017, para iOS9 en adelante ,

en general, solo haz estas dos cosas:

  1. elija "Texto atribuido"
  2. en la ventana emergente "Salto de línea", seleccione "Ajuste de línea"
jessecurry
fuente
55
Tenga en cuenta que estas tareas están en desuso en iOS 6. Consulte la respuesta a continuación de @NiKKi para obtener la sintaxis actualizada.
Aaron Brager el
66
Para que la gente sepa: esto no funciona si estás usando NSAttributedString
The dude
29
En realidad, solo agregue button.titleLabel.numberOfLines = 0; Eso hará que las líneas sean ilimitadas. Y button.titleLabel.textAlignment = NSTextAlignmentLeft; si desea texto justificado a la izquierda ya que el título está centrado por defecto.
RyJ
44
En rápidobutton.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping
Robert
1
Gracias @Robert por la rápida solución. También se puede utilizar la inferencia y omitir el NSLineBreakMode, así: button.titleLabel?.lineBreakMode = .ByWordWrapping.
mjswensen
145

La respuesta seleccionada es correcta, pero si prefiere hacer este tipo de cosas en Interface Builder, puede hacerlo:

Foto

Adam Waite
fuente
58
Gracias a mi yo de 24 años por escribir esta respuesta, de mi yo de 28 años.
Adam Waite
55
Y para seguir con la configuración única de Interface Builder, uno debe configurar titleLabel.textAlignmentcon el Numbervalor 1in User Defined Runtime Attributedpara centrar el texto
AnthoPak
2
¡Gracias a tu yo de 28 años de mi yo de 24 años!
Daniel Springer
59

Si desea agregar un botón con el título centrado con varias líneas, establezca la configuración de Interface Builder para el botón:

[ aquí]

usuario5440039
fuente
¿Puede agregar una descripción textual de cómo lograr esto? Una imagen es excelente, pero si no está disponible, su respuesta será inútil.
Rory McCrossan
1
@RoryMcCrossan Hola, agregué una imagen arriba. ¿Puedes verlo? Tenga en cuenta que Xcode 7 todavía tiene errores, por lo que el título del botón puede desaparecer. Sin embargo, una vez que ejecute la aplicación obtendrá el resultado deseado.
user5440039
2
Esta es la mejor respuesta en este hilo, en realidad. Funciona de inmediato y muestra texto centrado de IB. ¡Gracias!
RainCast
@ user5440039 Gracias por la excelente respuesta. No es necesario agregar una descripción textual.
Fattie
54

Para iOS 6:

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

Como

UILineBreakModeWordWrap and UITextAlignmentCenter

están en desuso en iOS 6 en adelante.

NiKKi
fuente
En NSText.hel interior Foundationno hay agregado en desuso. Está disponible desde 6.0 pero no en desuso.
Alex Cio
En rápido: button.titleLabel?.lineBreakMode = NSLineBreakMode.ByWordWrapping button.titleLabel?.textAlignment = NSTextAlignment.Center
jcity
30

Para repetir la sugerencia de Roger Nolan, pero con código explícito, esta es la solución general:

button.titleLabel?.numberOfLines = 0
baudot
fuente
21

SWIFT 3

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

En Xcode 9.3 puedes hacerlo usando el guión gráfico como se muestra a continuación,

ingrese la descripción de la imagen aquí

Debe configurar el texto del título del botón Alineación al centro

button.titleLabel?.textAlignment = .center

No es necesario establecer el texto del título con una nueva línea (\ n) como a continuación,

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

Simplemente establezca el título,

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

Aquí está el resultado,

ingrese la descripción de la imagen aquí

Nazrul Islam
fuente
myButton.titleLabel.textAlignment = NSTextAlignmentCenterdeclaración también es necesaria.
tounaobun
11

Hay una manera mucho más fácil:

someButton.lineBreakMode = UILineBreakModeWordWrap;

(Editar para iOS 3 y posterior :)

someButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
Valerii Hiora
fuente
44
UIButton.lineBreakMode fue desaprobado en 3.0, por lo que ya no es una buena opción.
Christopher Pickslay
2
lineBreakMode está en desuso solo como una propiedad directa de UIButton. Se nos dirige a "Usar la propiedad lineBreakMode de titleLabel en su lugar". Edité la respuesta de Valerii en consecuencia. Sigue siendo una buena opción.
Wienke
11

Alinear a la izquierda en iOS7 con autolayout:

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

Tuve un problema con el diseño automático, después de habilitar varias líneas, el resultado fue el siguiente: por lo que el tamaño no afecta el tamaño del botón que he agregado en función de (en este caso, contentEdgeInsets fue (10, 10, 10, 10 ) después de llamar : espero que te ayude (swift 5.0):
ingrese la descripción de la imagen aquí
titleLabel
ConstraintscontentEdgeInsets
makeMultiLineSupport()
ingrese la descripción de la imagen aquí

extension UIButton {

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

}
Amir Khorsandi
fuente
Funciona perfecto en Swift 5. También agregué mi función para centrar el texto en el botón. func centerTextInButton () {guard let titleLabel = titleLabel else {return} titleLabel.textAlignment = .center}
ShadeToD
Gracias. Esa es una buena solución.
Echelon
Descubrí que no es necesario agregar restricción, solo configure el texto después de establecer lineBreakMode / textAlignment / numberOfLines. Y también establezca titleEdgeInsets para el recuadro superior e inferior.
Bill Chan
@BillChan desafortunadamente eso no funciona en todos los casos
Amir Khorsandi
7

En primer lugar, debe tener en cuenta que UIButton ya tiene un UILabel dentro. Puedes configurarlo usando –setTitle:forState:.

El problema con su ejemplo es que necesita establecer la numberOfLinespropiedad de UILabel en algo diferente a su valor predeterminado de 1. También debe revisar la lineBreakModepropiedad.

Rog
fuente
Soy consciente de la propiedad del título, pero por lo que puedo decir, es imposible configurarlo para usar más de una línea, de ahí este enfoque. Si desactivo la imagen de fondo, aparece mi UILabel, lo que me sugiere un error en bringSubviewToFront o en el propio UIButton.
Owain Hunt
4

Para aquellos que usan el guión gráfico de Xcode 4, puede hacer clic en el botón, y en el panel derecho de Utilidades, debajo del Inspector de atributos, verá una opción para Salto de línea. Elija Word Wrap, y debería estar listo para comenzar.

Jack
fuente
4

Las respuestas aquí le dicen cómo lograr el título del botón multilínea mediante programación.

Solo quería agregar que si está usando guiones gráficos, puede escribir [Ctrl + Enter] para forzar una nueva línea en el campo de título de un botón.

HTH

usuario2338871
fuente
4

Tienes que agregar este código:

buttonLabel.titleLabel.numberOfLines = 0;
Pablo Blanco
fuente
3

En cuanto a la idea de Brent de poner el título UILabel como vista de hermano, no me parece una muy buena idea. Sigo pensando en problemas de interacción con el UILabel debido a que sus eventos táctiles no llegan a la vista del UIButton.

Por otro lado, con un UILabel como subvista del UIButton, me siento bastante cómodo sabiendo que los eventos táctiles siempre se propagarán a la supervista del UILabel.

Tomé este enfoque y no noté ninguno de los problemas reportados con backgroundImage. Agregué este código en -titleRectForContentRect: de una subclase UIButton, pero el código también se puede colocar en la rutina de dibujo de la supervista UIButton, que en ese caso reemplazará todas las referencias a sí mismo con la variable UIButton.

#define TITLE_LABEL_TAG 1234

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

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

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


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

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

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

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

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

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

Me tomé el tiempo y escribí un poco más sobre esto aquí . Allí, también apunto una solución más corta, aunque no se ajusta a todos los escenarios e involucra el pirateo de algunas vistas privadas. También allí, puede descargar una subclase de UIButton lista para ser utilizada.

jpedroso
fuente
3

Si usa el diseño automático en iOS 6, es posible que también deba configurar la preferredMaxLayoutWidthpropiedad:

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

Establecer lineBreakMode en NSLineBreakByWordWrapping (ya sea en IB o código) hace que la etiqueta del botón sea multilínea, pero no afecta el marco del botón.

Si el botón tiene un título dinámico, hay un truco: coloque UILabel oculto con la misma fuente y ate su altura a la altura del botón con diseño; cuando establece el texto en el botón y la etiqueta y la distribución automática hará todo el trabajo.

Nota

La altura del tamaño intrínseco del botón de una línea es más grande que la de la etiqueta, por lo que para evitar que la altura de la etiqueta se reduzca, la prioridad de contenido vertical debe ser mayor que la resistencia de compresión de contenido vertical del botón.

Varrry
fuente
3

En Swift 5.0 y Xcode 10.2

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

En su llamada de ViewController así

button.btnMultipleLines()//This is your button
iOS
fuente
3

Swift 5, para texto de varias líneas en UIButton

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

Funciona perfectamente

Agregue para usar esto con un archivo de configuración como Plist, debe usar CDATA para escribir el título multilínea, así:

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

Si usa el diseño automático.

button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.numberOfLines = 2
Sourabh Sharma
fuente
2

swift 4.0

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

En estos días, si realmente necesita que este tipo de cosas sean accesibles en el generador de interfaces caso por caso, puede hacerlo con una extensión simple como esta:

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

Luego, simplemente puede establecer numberOfLines como un atributo en cualquier subclase UIButton o UIButton como si fuera una etiqueta. Lo mismo ocurre con una gran cantidad de otros valores generalmente inaccesibles, como el radio de la esquina de la capa de una vista o los atributos de la sombra que proyecta.

Ceniza
fuente
0

Tira tu propio botón de clase. Es, con mucho, la mejor solución a largo plazo. UIButton y otras clases de UIKit son muy restrictivas en cómo puede personalizarlas.


fuente
0
self.btnError.titleLabel?.lineBreakMode = NSLineBreakMode.byWordWrapping

self.btnError.titleLabel?.textAlignment = .center

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

Incorporé la respuesta de jessecurry en STAButton, que es parte de mi biblioteca de código abierto STAControls . Actualmente lo uso dentro de una de las aplicaciones que estoy desarrollando y funciona para mis necesidades. Siéntase libre de abrir problemas sobre cómo mejorarlo o enviarme solicitudes de extracción.

Persona maravillosa
fuente
0

Para corregir el espacio de la etiqueta del título en el botón, establezca titleEdgeInsets y otras propiedades antes de setTitle:

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

PD: probé establecer titleLabel? .TextAlignment no es necesario y el título se alinea .natural.

Bill Chan
fuente
-4

Aunque está bien agregar una subvista a un control, no hay garantía de que realmente funcione, porque el control podría no esperar que esté allí y, por lo tanto, podría comportarse mal. Si puede salirse con la suya, simplemente agregue la etiqueta como una vista similar del botón y configure su marco para que se superponga al botón; siempre que esté configurado para aparecer en la parte superior del botón, nada de lo que pueda hacer el botón lo ocultará.

En otras palabras:

[button.superview addSubview:myLabel];
myLabel.center = button.center;
Brent Royal-Gordon
fuente
2
Por favor, si sugiere este tipo de enfoque feo, al menos no cometa errores en su código de ejemplo :). La etiqueta no debe tener el mismo centro que el botón si es una subvista. Utilice myLabel.frame = button.bounds; o myLabel.center = cgPointMake (button.frame.size.with * 0.5, button.frame.size.height * 0.5)
JakubKnejzlik
1
@GrizzlyNetch Recomiendo específicamente no agregarlo como una subvista del botón. addSubview:aquí lo agrega a buttonla supervista, lo que lo convierte en un hermano del botón, por lo que hacer coincidir sus centros es correcto.
Brent Royal-Gordon
Ah, mi error! Tienes razón, me he perdido el hecho "pequeño" de que está en la supervista: D ... lo siento :)
JakubKnejzlik
Que no es exactamente lo que se pregunta, esta solución sería una mala manera de implementar la pregunta ya que el botón del elemento tiene un título.
Pablo Blanco
Este fue un consejo razonable en iPhone OS 2.0. Hay formas mucho mejores de hacerlo por ahora. : ^)
Brent Royal-Gordon el