iOS: redimensionar o UIButton de acordo com o tamanho do texto

134

No construtor de interfaces, pressionar Command+ =redimensionará um botão para ajustar seu texto. Fiquei me perguntando se isso era possível fazer programaticamente antes que o botão fosse adicionado à exibição.

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
fonte
1
Nota para esta pergunta muito antiga: hoje em dia (2017), basta definir a restrição como "maior que ou igual" e faz tudo. (Resposta Observe a seguir por Tad.)
Fattie
@Fattie Uma restrição " maior que ou igual " não é suficiente para compatibilidade com o AutoLayout. Atualmente (2017+), deve-se colocar o botão dentro de algo que seja redimensionado corretamente (resposta Bartłomiej Semańczyk) ou subclassificar o UIButton, como em stackoverflow.com/a/50575588/1033581
Cœur
oi @ Cœur - existem muitas sutilezas na execução automática. É possível que você esteja pensando em uma situação um pouco diferente? No entanto, felizmente, você está totalmente errado - apenas tente !! Basta colocar um UIButton em um controlador de exibição. obviamente, defina o centro horizontal e vertical. E adicione uma restrição de largura => 100. funciona perfeitamente no iOS mais recente.
Fattie
1
(Para quem é iniciante no autolayout pesquisando aqui ........... apenas no TBC, não é necessário adicionar nada, e o UIButton (também UILabel, etc) será redimensionado automaticamente para a largura com alterações de texto. restrição corretamente a posição H / V.)
Fattie
Demonstração @Fattie . Em particular, no tempo de execução, ele se comporta assim: github.com/Coeur/autolayoutButton/blob/master/buttonSelfSizing/…
Cœur

Respostas:

1

No Xcode 4.5 e superior, isso agora pode ser feito usando ' Layout automático / restrições '.

As principais vantagens são as seguintes:

  1. Você não precisa definir quadros de forma programática!
  2. Se bem feito, você não precisa se preocupar em redefinir os quadros para alterar a orientação.
  3. Além disso, as alterações do dispositivo não precisam incomodá-lo (leia, não é necessário codificar separadamente para diferentes tamanhos de tela).

Algumas desvantagens:

  1. Não é compatível com versões anteriores - funciona apenas para iOS 6 e superior.
  2. Precisa se familiarizar (mas economizará tempo mais tarde).

O mais legal é que nos concentramos em declarar uma intenção como:

  • Eu quero que esses dois botões tenham a mesma largura; ou
  • Eu preciso que essa visão seja centralizada verticalmente e se estenda a um máximo de 10 pontos da borda da superview; ou mesmo
  • Quero que este botão / etiqueta seja redimensionado de acordo com a etiqueta que está sendo exibida!

Aqui está um tutorial simples para ser apresentado ao layout automático.

Para mais detalhes .

No início, leva algum tempo, mas com certeza parece que valerá a pena.

codeburn
fonte
3
Eu quase perdi essa resposta - ela realmente precisa de muito mais votos. Com esta solução, pode-se evitar a incorporação de nomes e tamanhos de fontes ou chamadas apenas para o iOS 7. Simplesmente defino a restrição do lado que eu queria que meu UIButton se estendesse e é isso.
Carl
Quase uma solução perfeita, infelizmente não funcionará se você usar titleEdgeInsets: /
Zoltán
130
Essa é uma resposta ruim - detalha os méritos do AutoLayout em vez de como o AutoLayout pode ser aplicado nessa situação para resolver o problema do solicitante.
mattsven
4
Fiquei me perguntando se isso era possível fazer programaticamente diz que a resposta ...
Juan Boero
As restrições de layout automático do @JuanPabloBoeroAlvarez podem ser especificadas no Interface Builder e / ou programaticamente.
Slipp D. Thompson
130

No UIKit, há adições à classe NSString para obter de um determinado objeto NSString o tamanho que ele assumirá quando renderizado em uma determinada fonte.

Docs estava aqui . Agora está aqui em Descontinuado.

Em resumo, se você for:

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

... você definirá o quadro do botão para a altura e largura da string que está renderizando.

Você provavelmente desejará experimentar algum espaço de buffer em torno desse CGSize, mas estará começando no lugar certo.

Dan Ray
fonte
4
Parece que o preenchimento padrão do iOS é 12px, 8px.
Ben Lachman
19
sizeWithFont foi descontinuado no iOS 7.0. Use sizeWithAttributes: em vez
carmen_munich
6
Eu sugiro fortemente que não brinque mais com quadros, mas SOMENTE com restrições.
Gil Areia
@ GilSand Existe uma maneira de usar o construtor de interface para isso?
huggie
Sim, uma pesquisa no Google deve dar-lhe uma abundância de resultados úteis
Gil Areia
101

A maneira de fazer isso no código é:

[button sizeToFit];

Se você estiver na subclasse e quiser adicionar regras extras, poderá substituir:

- (CGSize)sizeThatFits:(CGSize)size;
Daniel Wood
fonte
5
sizeToFit funciona perfeitamente em controles padrão como UIButton ou UILabel. Se você estiver fazendo isso em um UIView personalizado, precisará implementar -(CGSize)sizeThatFits:(CGSize)size.
Cameron Lowell Palmer
4
Funciona apenas se você o colocar depois de definir o texto, caso contrário, não há informações suficientes sobre o tamanho que deve ser: [button setTitle:@"Your text here" forState:UIControlStateNormal]; [button sizeToFit]; Além disso, se você atualizar o texto posteriormente, não se esqueça de chamá-lo novamente.
Juan Boero
Certifique-se de definir o texto apenas dessa maneira: [myButton setTitle: @ "meu título" paraState: UIControlStateNormal]; Para mim, não funcionou até repetir essa afirmação.
Darius Miliauskas
7
sizeToFit()não respeita as inserções da borda do título.
precisa saber é
32

Se seu botão foi criado com o Interface Builder e você está alterando o título no código, você pode fazer isso:

[self.button setTitle:@"Button Title" forState:UIControlStateNormal];
[self.button sizeToFit];
guptron
fonte
1
E você pode fazer isso mesmo que seu botão tenha sido criado com código.
Markus
22

Rápido:

O importante aqui é quando você chamará o .sizeToFit(), deve ser depois de definir o texto a ser exibido para que ele possa ler o tamanho necessário; se você atualizar o texto posteriormente, ligue novamente.

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

Para fazer isso usando o layout automático, tente definir uma restrição de largura variável:

Restrição de largura

Você também pode precisar ajustar Content Hugging Prioritye Content Compression Resistance Priorityobter os resultados necessários.


O UILabel é automaticamente dimensionado automaticamente :

Este UILabel é simplesmente definido para ser centralizado na tela (apenas duas restrições, horizontal / vertical):

Altera larguras totalmente automaticamente:

Você não precisa definir nenhuma largura ou altura - é totalmente automático.

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Observe que os pequenos quadrados amarelos são simplesmente anexados ("espaçamento" de zero). Eles se movem automaticamente conforme o UILabel é redimensionado.

Adicionar uma restrição "> =" define uma largura mínima para o UILabel:

insira a descrição da imagem aqui

Tad
fonte
16

Se você deseja redimensionar o texto em oposição ao botão, pode 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
fonte
3
* ajustaFontSizeToFitWidth
Daniel Bang
8

sizeToFit não funciona corretamente. em vez de:

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

você também pode adicionar contentInsetao botão:

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

Hashem Aboonajmi
fonte
Nos documentos da Apple sobre sizeToFit: // chama sizeThatFits: com os limites de exibição atuais e altera o tamanho dos limites. Para que funcione corretamente, basta configurá-lo depois de definir o texto.
27417 Markus
5

Simplesmente:

  1. Crie UIViewcomo wrapper com layout automático para visualizações ao redor.
  2. Coloque UILabeldentro desse invólucro. Adicione restrições que colem seu rótulo nas bordas do invólucro.
  3. Coloque UIButtondentro do seu invólucro e adicione as mesmas restrições que você fez UILabel.
  4. Aproveite o botão de tamanho automático junto com o texto.
Bartłomiej Semańczyk
fonte
1
Isso funciona bem com acessibilidade?
jasonszhao
4

Swift 4.2

Graças a Deus, isso resolveu. Depois de definir um texto para o botão, você pode recuperar o intrinsicContentSize, que é o tamanho natural de um UIView (o documento oficial está aqui ). Para o UIButton, você pode usá-lo como abaixo.

button.intrinsicContentSize.width

Para sua informação, ajustei a largura para torná-la adequada.

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

Simulador

UIButtons com intrinsicContentSize

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

nator333
fonte
1
Você poderia dar um pouco mais de contexto? Como sua linha de código se relaciona com o código da pergunta? Você também pode resumir o que o código que você sugere faz? Apenas fornecer o link como explicação não é o formato certo para este site (consulte meta.stackexchange.com/a/8259/266197 por motivos), mas se você adicionar alguma explicação e contexto, sua resposta se tornará mais valiosa!
NOHS
3

Tenho algumas necessidades adicionais relacionadas a este post que sizeToFitnão solucionam. Eu precisava manter o botão centralizado em seu local original, independentemente do texto. Também nunca quero que o botão seja maior que o tamanho original.

Então, eu layout o botão para seu tamanho máximo, salve esse tamanho no Controller e use o seguinte método para dimensionar o botão quando o texto for alterado:

+ (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
fonte
1

Essa classe de botão com redimensionamento automático de altura para texto (para Xamarin, mas pode ser reescrita para outro 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
fonte
1

Por alguma razão, func sizeToFit()não funciona para mim. Minha configuração é que estou usando um botão dentro de um UITableViewCelle estou usando o layout automático.

O que funcionou para mim é:

  1. obter a restrição de largura
  2. obtenha a intrinsicContentSizelargura porque, de acordo com este documento, o layout automático não tem conhecimento do intrinsicContentSize. Defina a restrição de largura para a intrinsicContentSizelargura

insira a descrição da imagem aqui

Aqui estão dois títulos do Debug View Hierachry insira a descrição da imagem aqui

insira a descrição da imagem aqui

Loozie
fonte
0

Para ser sincero, acho que é realmente uma pena que não haja uma caixa de seleção simples no storyboard para dizer que você deseja redimensionar os botões para acomodar o texto. Bem, tanto faz.

Aqui está a solução mais simples usando o storyboard.

  1. Coloque o UIView e coloque restrições para ele. Exemplo: insira a descrição da imagem aqui
  2. Coloque o UILabel dentro do UIView. Defina restrições para anexá-lo às bordas do UIView.

  3. Coloque seu UIButton dentro do UIView. Defina as mesmas restrições para anexá-lo às bordas do UIView.

  4. Defina 0 para o número de linhas do UILabel. insira a descrição da imagem aqui

  5. Configure as tomadas.

    @IBOutlet var button: UIButton!
    @IBOutlet var textOnTheButton: UILabel!
  1. Obtenha um título longo, longo e longo.

    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. Em viewDidLoad, defina o título para UILabel e UIButton.

    override func viewDidLoad() {
        super.viewDidLoad()
        textOnTheButton.text = someTitle
        button.setTitle(someTitle, for: .normal)
        button.titleLabel?.numberOfLines = 0
    }
  1. Execute-o para garantir que o botão seja redimensionado e possa ser pressionado.

insira a descrição da imagem aqui

Shalugin
fonte