UIButton que cambia el tamaño para ajustarse a su etiqueta de título


Tengo un UIButton que agrego a la vista de mi controlador de vista en un guion gráfico. Agrego restricciones de centrado para posicionarlo y restricciones de espacio principales para limitar su ancho. En el código agrego:

self.button.titleLabel.numberOfLines = 0;
self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
[self.button setTitle:@"A real real real real real real real real long long name." forState:UIControlStateNormal];
self.button.backgroundColor = [UIColor redColor];
self.button.titleLabel.backgroundColor = [UIColor blueColor];

El resultado se muestra a continuación:

introduzca la descripción de la imagen aquí

Quiero que el botón se ajuste a su contenido. ¿Cómo puedo hacer esto?

He intentado

[self.button sizeToFit];

Y he intentado establecer el contenido abrazos y la resistencia a la compresión autolayout restricciones prioridades a requerido.

He también intentó establecer explícitamente contentEdgeInsets y titleEdgeInsets a UIEdgeInsetsZero y llamar a invalidateIntrinsicContentSize.

También he notado que si pongo caracteres de nueva línea en la cadena de título, el botón parece cambiar de tamaño para adaptarse a su contenido.

Me estoy ejecutando en Xcode 6 y iOS 8 en el simulador de iPhone 6.

Author: shim, 2014-12-03

11 answers

He conseguido que esto funcione, pero tienes que usar un botón personalizado, no un tipo de sistema. Dale al botón restricciones de anchura y altura, y haz un IBOutlet a la restricción de altura (heightCon en mi código) para que puedas ajustarla en código.

- (void)viewDidLoad {
    [super viewDidLoad];
    self.button.titleLabel.numberOfLines = 0;
    self.button.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
    [self.button setTitle:@"A real real real real real real real real long long name." forState:UIControlStateNormal];
    [self.button addTarget:self action:@selector(doStuff:) forControlEvents:UIControlEventTouchUpInside];
    self.button.backgroundColor = [UIColor redColor];
    self.button.titleLabel.backgroundColor = [UIColor blueColor];
    [self.button layoutIfNeeded]; // need this to update the button's titleLabel's size
    self.heightCon.constant = self.button.titleLabel.frame.size.height;
}

Después De Editar:

Encontré que también puedes hacer esto de manera más simple, y con un botón del sistema si haces una subclase, y usas este código,

@implementation RDButton

-(CGSize)intrinsicContentSize {
    return CGSizeMake(self.frame.size.width, self.titleLabel.frame.size.height);
}

El método intrinsicContentSize sobrescrito se llama cuando se establece el titular. No debe establecer una restricción de altura en este caso.

 17
Author: rdelmar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-03 05:05:13

Swift 4.x versión de respuesta de Kubba :

Necesita actualizar Salto de líneacomo Clip/WordWrap/ en el creador de interfaces a los botones correspondientes.

class ResizableButton: UIButton {
    override var intrinsicContentSize: CGSize {
       let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.width, height: .greatestFiniteMagnitude)) ?? .zero
       let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)

       return desiredButtonSize
    }
}
 16
Author: A. Buksha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-21 07:56:37

Tengo el mismo problema. Solo sucede si UIButton's titleLabel tiene más de una línea. Es un error en UIKit?

Mi solución rápida:

class ResizableButton: UIButton {    
    override var intrinsicContentSize: CGSize {
        let labelSize = titleLabel?.sizeThatFits(CGSize(width: frame.size.width, height: CGFloat.greatestFiniteMagnitude)) ?? .zero
        let desiredButtonSize = CGSize(width: labelSize.width + titleEdgeInsets.left + titleEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom)

        return desiredButtonSize
    }
}
 11
Author: Kubba,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-03-05 06:53:04

Luché con esto durante un tiempo y terminé haciéndolo funcionar mediante la subclase UIButton y la adición de estas dos funciones

class GoalsButton: UIButton {

    override var intrinsicContentSize: CGSize {
        return self.titleLabel!.intrinsicContentSize
    }

    // Whever the button is changed or needs to layout subviews,
    override func layoutSubviews() {
        super.layoutSubviews()
        titleLabel?.preferredMaxLayoutWidth = self.titleLabel!.frame.size.width
    }
}
 3
Author: Lord Fresh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-10 15:57:54

Estoy lejos de mi computadora, así que no puedo agregar el código en este momento, pero he encontrado una solución a esto antes.

Lo que puede hacer es crear un UILabel y agregar un UITapGestureRecognizer a la etiqueta. Haz lo que quieras para la acción del botón manejando el evento tap. Y también asegúrese de habilitar las interacciones del usuario en UILabel.

Esta etiqueta ahora se comportará como un botón de redimensionamiento automático.

 2
Author: Vinay Jain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-03 03:58:16

Lo que sugeriría es calcular el ancho del texto, y calcular el marco por ti mismo. No es complicado de todos modos, obtener el ancho del texto primero:

[NSString sizeWithFont:font];

Haga una operación de modificación y encontrará fácilmente el número de líneas para el texto.

Tenga en cuenta que este método es para iOS7 pre, para iOS 7 y después es posible que desee probar

[NSString sizeWithAttributes:aDictionary];
 1
Author: Xiangdong,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-03 04:22:16

[self.botón sizeToFit] debería funcionar si su diseño Automático está Desactivado. Si tiene que usar el diseño automático, entonces otras sugerencias (calcular el ancho de línea), etc., son más apropiadas.

 1
Author: Aamir,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-12-05 07:52:13

Yo tenía el mismo problema con UIButton con multilined de texto, y también tenía una imagen. Usé sizeThatFits: para calcular el tamaño, pero calculó la altura incorrecta.

No lo hice UIButtonTypeCustom, en lugar de eso llamé sizeThatFits: en el botón titleLabel con un tamaño con un ancho más pequeño (debido a la imagen en el botón):

CGSize buttonSize = [button sizeThatFits:CGSizeMake(maxWidth, maxHeight)];
CGSize labelSize = [button.titleLabel sizeThatFits:CGSizeMake(maxWidth - offset, maxHeight)]; // offset for image
buttonSize.height = labelSize.height;
buttonFrame.size = buttonSize;

Y luego usé la altura de ese tamaño para establecer el marco del botón correctamente, y FUNCIONÓ:)

Tal vez tengan algún error en el tamaño interno de UIButton.

 1
Author: BSevo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-07-01 08:20:40

Sobrescriba el -(CGSize)intrinsicContentSize en UIButton personalizado como se indica a continuación.

Objetivo-C:

-(CGSize)intrinsicContentSize {
    CGSize titleLabelIntrinsicSize = [self.titleLabel intrinsicContentSize];
    return CGSizeMake(titleLabelIntrinsicSize.width + self.contentEdgeInsets.left + self.contentEdgeInsets.right, titleLabelIntrinsicSize.height + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom);
}

Swfit :

override var intrinsicContentSize: CGSize {
        get {
            if let thisSize = self.titleLabel?.intrinsicContentSize {
                return CGSize(width: thisSize.width + self.contentEdgeInsets.left + self.contentEdgeInsets.right, height: thisSize.height + self.contentEdgeInsets.top + self.contentEdgeInsets.bottom)
            }
            return super.intrinsicContentSize
        }
    }
 0
Author: Bhavesh Patel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-31 07:39:09

Tuve que invalidar el tamaño de contenido intrínseco cuando se establecieron las vistas y luego calcular la altura del botón para la propiedad intrinsicContentSize.

Aquí está el código en Swift3 / Xcode 9

override func layoutSubviews() {
    self.invalidateIntrinsicContentSize()
    super.layoutSubviews()
}


override var intrinsicContentSize: CGSize {
    return CGSize(width: self.frame.size.width, height: titleLabel!.frame.size.height + contentEdgeInsets.top + contentEdgeInsets.bottom)
}
 0
Author: Maria,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-04 15:14:50
class SFResizableButton: UIButton {
  override var intrinsicContentSize: CGSize {
    get {
      var labelSize = CGSize.zero
        if let text = titleLabel?.text, let font = titleLabel?.font {
          labelSize.width = text.width(constrained: .greatestFiniteMagnitude, font: font)
        } else if let att = titleLabel?.attributedText {
          labelSize.width = att.width(constrained: .greatestFiniteMagnitude)
        }
      if let imageView = imageView {
        labelSize.width = labelSize.width + imageView.frame.width
      }
      let desiredButtonSize = CGSize(width: ceil(labelSize.width) + titleEdgeInsets.left + titleEdgeInsets.right + imageEdgeInsets.left + imageEdgeInsets.right, height: labelSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom + imageEdgeInsets.top + imageEdgeInsets.bottom)

        return desiredButtonSize
    }
  }
}
extesion String {
  func width(constrained height: CGFloat, font: UIFont) -> CGFloat {
    let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
      let boundingBox = (self as NSString).boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, attributes: [NSFontAttributeName: font], context: nil)

      return boundingBox.width
  }
}

extension NSAttributedString {
  func width(constrained height: CGFloat) -> CGFloat {
    let constraintRect = CGSize(width: .greatestFiniteMagnitude, height: height)
      let boundingBox = boundingRect(with: constraintRect, options: .usesLineFragmentOrigin, context: nil)

      return boundingBox.width
  }
}
 -1
Author: looseyi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-10 15:17:01