iOS: cambio de tamaño de UIButton según la longitud del texto

134

En el creador de interfaces, al mantener presionado Command+ se =cambiará el tamaño de un botón para que se ajuste a su texto. Me preguntaba si esto era posible hacerlo mediante programación antes de agregar el botón a la vista.

UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[button.titleLabel setFont:[UIFont fontWithName:@"Arial-BoldMT" size:12]];
[button addTarget:self action:@selector(buttonTapped:) forControlEvents:UIControlEventTouchUpInside];
// I need to know the width needed to accomodate NSStringVariable
[button setTitle:NSStringVariable forState:UIControlStateNormal]; 
// So that I can set the width property of the button before addSubview
[button setFrame:CGRectMake(10, 0, width, fixedHeight)];
[subNavigation addSubview:button];
Oh Danny Boy
fuente
1
Tenga en cuenta esta antigua pregunta: hoy en día (2017), simplemente establezca la restricción como "mayor o igual" y lo hace todo. (Observe la respuesta a continuación por Tad.)
Fattie
@Fattie Una restricción " mayor o igual " no es suficiente para la compatibilidad con AutoLayout. Hoy en día (2017+) uno debe envolver el botón dentro de algo que cambie de tamaño correctamente (respuesta de Bartłomiej Semańczyk), o uno debe subclasificar el UIButton, como en stackoverflow.com/a/50575588/1033581
Cœur
hola @ Cœur: hay muchas sutilezas en la distribución automática. ¿Es posible que estés pensando en una situación ligeramente diferente? Sin embargo, afortunadamente, estás totalmente equivocado, ¡ solo pruébalo ! Simplemente coloque un UIButton en un controlador de vista. obviamente establece el centro horizontal y vertical. Y luego agregue una restricción de ancho> = 100. Funciona perfectamente en el último iOS.
Fattie
1
(Para cualquier persona nueva en Google Autolayout ............ solo TBC no tiene que agregar nada en absoluto, y UIButton (también UILabel, etc.) cambiará el tamaño al ancho automáticamente con cambios de texto. Solo restringir adecuadamente la posición H / V.)
Fattie
Demostración de @Fattie . En particular, en tiempo de ejecución, se comporta así: github.com/Coeur/autolayoutButton/blob/master/buttonSelfSizing/…
Cœur

Respuestas:

1

En Xcode 4.5 y versiones posteriores, esto ahora se puede hacer usando ' Diseño automático / Restricciones '.

Las principales ventajas son que:

  1. ¡No necesita establecer marcos mediante programación!
  2. Si se hace correctamente, no necesita preocuparse por restablecer marcos para cambios de orientación.
  3. Además, los cambios en el dispositivo no tienen por qué molestarte (lee, no es necesario codificar por separado para diferentes tamaños de pantalla).

Algunas desventajas:

  1. No es compatible con versiones anteriores: funciona solo para iOS 6 y superior.
  2. Necesita familiarizarse (pero ahorrará tiempo más adelante).

Lo mejor es que podemos centrarnos en declarar una intención como:

  • Quiero que estos dos botones sean del mismo ancho; o
  • Necesito que esta vista esté centrada verticalmente y se extienda a un máximo de 10 puntos desde el borde de la supervista; o incluso,
  • ¡Quiero que este botón / etiqueta cambie de tamaño de acuerdo con la etiqueta que está mostrando!

Aquí hay un tutorial simple para iniciarse en el diseño automático.

Para más detalles .

Al principio lleva algo de tiempo, pero parece que valdrá la pena el esfuerzo.

codeburn
fuente
3
Casi me pierdo esta respuesta, realmente necesita muchos más votos. Con esta solución, se puede evitar incrustar nombres y tamaños de fuente o llamadas solo para iOS 7. Simplemente establezco la restricción del lado en el que quería que mi UIButton se extendiera y eso es todo.
Carl
Casi una solución perfecta, desafortunadamente no funciona si usa titleEdgeInsets: /
Zoltán
130
Esta es una respuesta pobre: ​​detalla los méritos de AutoLayout en lugar de cómo se puede aplicar AutoLayout en esta situación para resolver el problema del autor de la pregunta.
mattsven
44
Me preguntaba si esto era posible hacerlo programáticamente, dice la respuesta ...
Juan Boero
Las restricciones de diseño automático de @JuanPabloBoeroAlvarez se pueden especificar en Interface Builder y / o mediante programación.
Slipp D. Thompson
130

En UIKit, hay adiciones a la clase NSString para obtener de un objeto NSString dado el tamaño que tomará cuando se represente en una fuente determinada.

Docs estuvo aquí . Ahora está aquí en desuso.

En resumen, si vas:

CGSize stringsize = [myString sizeWithFont:[UIFont systemFontOfSize:14]]; 
//or whatever font you're using
[button setFrame:CGRectMake(10,0,stringsize.width, stringsize.height)];

... habrá configurado el marco del botón a la altura y el ancho de la cadena que está renderizando.

Probablemente quiera experimentar con un poco de espacio de búfer alrededor de ese CGSize, pero comenzará en el lugar correcto.

Dan Ray
fuente
44
Parece que el relleno predeterminado de iOS es 12px, 8px.
Ben Lachman
19
sizeWithFont está en desuso en iOS 7.0. Use sizeWithAttributes: en su lugar
carmen_munich
66
Recomiendo no jugar más con cuadros, sino SOLO con restricciones.
Gil Sand
@GilSand ¿Hay alguna manera de usar el generador de interfaces para esto?
huggie
Sí, una búsqueda en Google debería darle muchos resultados útiles
Gil Sand
101

La forma de hacer esto en código es:

[button sizeToFit];

Si está subclasificando y desea agregar reglas adicionales, puede anular:

- (CGSize)sizeThatFits:(CGSize)size;
Daniel Wood
fuente
55
sizeToFit funciona perfectamente en controles estándar como UIButton o UILabel. Si lo está haciendo en una UIView personalizada, deberá implementarla -(CGSize)sizeThatFits:(CGSize)size.
Cameron Lowell Palmer
44
Solo funciona si lo coloca después de configurar el texto; de lo contrario, no hay suficiente información sobre cuán grande debería ser: [button setTitle:@"Your text here" forState:UIControlStateNormal]; [button sizeToFit]; Además, si actualiza el texto más tarde, no olvide volver a llamarlo.
Juan Boero
Asegúrese de configurar el texto solo de esa manera: [myButton setTitle: @ "my title" forState: UIControlStateNormal]; Para mí no funcionó hasta que repetí esa declaración.
Darius Miliauskas
77
sizeToFit()no respeta las inserciones del borde del título.
Kelin
32

Si su botón se creó con Interface Builder y está cambiando el título del código, puede hacer esto:

[self.button setTitle:@"Button Title" forState:UIControlStateNormal];
[self.button sizeToFit];
guptron
fuente
1
Y puede hacerlo incluso si su botón se hizo con código.
Markus
22

Rápido:

Lo importante aquí es cuándo llamarás al .sizeToFit(), debe ser después de configurar el texto para que se muestre para que pueda leer el tamaño necesario, si luego actualizas el texto, vuelve a llamarlo.

var button = UIButton()
button.setTitle("Length of this text determines size", forState: UIControlState.Normal)
button.sizeToFit()
self.view.addSubview(button)
Juan boero
fuente
18

Para lograr esto usando autolayout, intente establecer una restricción de ancho variable:

Restricción de ancho

También es posible que necesite ajustar su Content Hugging Priorityy Content Compression Resistance Priorityobtener los resultados que necesita.


UILabel es completamente automático :

Este UILabel simplemente está configurado para centrarse en la pantalla (solo dos restricciones, horizontal / vertical):

Cambia los anchos de forma totalmente automática:

No es necesario establecer ningún ancho o alto, es totalmente automático.

ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Observe que los pequeños cuadrados amarillos simplemente se unen ("espaciado" de cero). Se mueven automáticamente a medida que UILabel cambia de tamaño.

Agregar una restricción "> =" establece un ancho mínimo para UILabel:

ingrese la descripción de la imagen aquí

Tad
fuente
16

Si desea cambiar el tamaño del texto en lugar del botón, puede usar ...

button.titleLabel.adjustsFontSizeToFitWidth = YES;
button.titleLabel.minimumScaleFactor = .5;
// The .5 value means that I will let it go down to half the original font size 
// before the texts gets truncated

// note, if using anything pre ios6, you would use I think
button.titleLabel.minimumFontSize = 8;
Logan
fuente
3
* ajustaFontSizeToFitWidth
Daniel Bang
8

sizeToFit no funciona correctamente. en lugar:

myButton.size = myButton.sizeThatFits(CGSize.zero)

También puedes agregar contentInsetal botón:

myButton.contentEdgeInsets = UIEdgeInsetsMake(8, 8, 4, 8)

Hashem Aboonajmi
fuente
De los documentos de Apple sobre sizeToFit: // llama a sizeThatFits: con los límites de la vista actual y cambia el tamaño de los límites. Para que funcione correctamente, solo tiene que configurarlo después de configurar el texto.
Markus
5

Simplemente:

  1. Cree UIViewcomo envoltorio con diseño automático para vistas alrededor.
  2. Poner UILabel dentro de esa envoltura. Agregue restricciones que peguen su etiqueta a los bordes del contenedor.
  3. Colóquelo UIButtondentro de su contenedor, luego simplemente agregue las mismas restricciones que lo hizo para UILabel.
  4. Disfruta de tu botón autosized junto con el texto.
Bartłomiej Semańczyk
fuente
1
¿Esto juega bien con la accesibilidad?
jasonszhao
4

Swift 4.2

Gracias a Dios, esto está resuelto. Después de configurar un texto para el botón, puede recuperar intrinsicContentSize, que es el tamaño natural de una UIView (el documento oficial está aquí ). Para UIButton, puede usarlo como a continuación.

button.intrinsicContentSize.width

Para su información, ajusté el ancho para que se vea correctamente.

button.frame = CGRect(fx: xOffset, y: 0.0, width: button.intrinsicContentSize.width + 18, height: 40)

Simulador

Botones UIB con intrinsicContentSize

Fuente: https://riptutorial.com/ios/example/16418/get-uibutton-s-size-strictly-based-on-its-text-and-font

nator333
fuente
1
¿Podrías dar un poco más de contexto? ¿Cómo se relaciona su línea de código con el código de la pregunta? ¿Podría también resumir lo que hace el código que sugiere? Solo proporcionar el enlace como explicación no es el formato correcto para este sitio (consulte meta.stackexchange.com/a/8259/266197 por razones), pero si agrega alguna explicación y contexto, su respuesta sería más valiosa.
NOh
3

Tengo algunas necesidades adicionales relacionadas con esta publicación que sizeToFitno resuelve. Necesitaba mantener el botón centrado en su ubicación original, independientemente del texto. Tampoco quiero que el botón sea más grande que su tamaño original.

Entonces, diseño el botón para su tamaño máximo, guardo ese tamaño en el Controlador y uso el siguiente método para cambiar el tamaño del botón cuando cambia el texto:

+ (void) sizeButtonToText:(UIButton *)button availableSize:(CGSize)availableSize padding:(UIEdgeInsets)padding {    

     CGRect boundsForText = button.frame;

    // Measures string
    CGSize stringSize = [button.titleLabel.text sizeWithFont:button.titleLabel.font];
    stringSize.width = MIN(stringSize.width + padding.left + padding.right, availableSize.width);

    // Center's location of button
    boundsForText.origin.x += (boundsForText.size.width - stringSize.width) / 2;
    boundsForText.size.width = stringSize.width;
    [button setFrame:boundsForText];
 }
raider33
fuente
1

Esta clase de botón con altura de tamaño automático para texto (para Xamarin pero se puede reescribir para otro idioma)

[Register(nameof(ResizableButton))]
public class ResizableButton : UIButton
{
    NSLayoutConstraint _heightConstraint;
    public bool NeedsUpdateHeightConstraint { get; private set; } = true;

    public ResizableButton(){}

    public ResizableButton(UIButtonType type) : base(type){}

    public ResizableButton(NSCoder coder) : base(coder){}

    public ResizableButton(CGRect frame) : base(frame){}

    protected ResizableButton(NSObjectFlag t) : base(t){}

    protected internal ResizableButton(IntPtr handle) : base(handle){}

    public override void LayoutSubviews()
    {
        base.LayoutSubviews();
        UpdateHeightConstraint();
        InvalidateIntrinsicContentSize();
    }

    public override void SetTitle(string title, UIControlState forState)
    {
        NeedsUpdateHeightConstraint = true;
        base.SetTitle(title, forState);
    }

    private void UpdateHeightConstraint()
    {
        if (!NeedsUpdateHeightConstraint)
            return;
        NeedsUpdateHeightConstraint = false;
        var labelSize = TitleLabel.SizeThatFits(new CGSize(Frame.Width - TitleEdgeInsets.Left - TitleEdgeInsets.Right, float.MaxValue));

        var rect = new CGRect(Frame.X, Frame.Y, Frame.Width, labelSize.Height + TitleEdgeInsets.Top + TitleEdgeInsets.Bottom);

        if (_heightConstraint != null)
            RemoveConstraint(_heightConstraint);

        _heightConstraint = NSLayoutConstraint.Create(this, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1, rect.Height);
        AddConstraint(_heightConstraint);
    }

     public override CGSize IntrinsicContentSize
     {
         get
         {
             var labelSize = TitleLabel.SizeThatFits(new CGSize(Frame.Width - TitleEdgeInsets.Left - TitleEdgeInsets.Right, float.MaxValue));
             return new CGSize(Frame.Width, labelSize.Height + TitleEdgeInsets.Top + TitleEdgeInsets.Bottom);
         }
     }
}
elamaunt
fuente
1

Por alguna razón, func sizeToFit()no funciona para mí. Mi configuración es que estoy usando un botón dentro de UITableViewCellay estoy usando el diseño automático.

Lo que funcionó para mí es:

  1. obtener la restricción de ancho
  2. obtenga el intrinsicContentSizeancho porque de acuerdo con este documento, el diseño automático no conoce el intrinsicContentSize. Establecer la restricción de intrinsicContentSizeancho al ancho

ingrese la descripción de la imagen aquí

Aquí hay dos títulos de Debug View Hierachry ingrese la descripción de la imagen aquí

ingrese la descripción de la imagen aquí

Loozie
fuente
0

Para ser sincero, creo que es realmente una pena que no haya una casilla de verificación simple en el guión gráfico para decir que desea cambiar el tamaño de los botones para acomodar el texto. Bueno lo que sea.

Aquí está la solución más simple usando storyboard.

  1. Coloque UIView y ponga restricciones para ello. Ejemplo: ingrese la descripción de la imagen aquí
  2. Coloque UILabel dentro de UIView. Establezca restricciones para adjuntarlo a los bordes de UIView.

  3. Coloque su UIButton dentro de UIView. Establezca las mismas restricciones para adjuntarlo a los bordes de UIView.

  4. Establezca 0 para el número de líneas de UILabel. ingrese la descripción de la imagen aquí

  5. Configurar los puntos de venta.

    @IBOutlet var button: UIButton!
    @IBOutlet var textOnTheButton: UILabel!
  1. Consigue un título largo, largo, largo.

    let someTitle = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
  1. En viewDidLoad establezca el título tanto para UILabel como para UIButton.

    override func viewDidLoad() {
        super.viewDidLoad()
        textOnTheButton.text = someTitle
        button.setTitle(someTitle, for: .normal)
        button.titleLabel?.numberOfLines = 0
    }
  1. Ejecútelo para asegurarse de que el botón cambia de tamaño y se puede presionar.

ingrese la descripción de la imagen aquí

Shalugin
fuente