iOS: UIButton est redimensionné en fonction de la longueur du texte

134

Dans le générateur d'interface, maintenir Command+ =redimensionnera un bouton pour l'adapter à son texte. Je me demandais si cela était possible de le faire par programme avant que le bouton ne soit ajouté à la vue.

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
la source
1
Remarque pour cette très vieille question: de nos jours (2017), il suffit de définir la contrainte comme "supérieure ou égale" et elle fait tout. (Remarquez la réponse ci-dessous par Tad.)
Fattie
@Fattie Une contrainte " supérieure ou égale " n'est pas suffisante pour la compatibilité avec AutoLayout. De nos jours (2017+), il faut soit envelopper le bouton dans quelque chose qui se redimensionne correctement (réponse de Bartłomiej Semańczyk), soit sous-classer l'UIButton, comme dans stackoverflow.com/a/50575588/1033581
Cœur
salut @ Cœur - la mise en page automatique comporte de nombreuses subtilités. Il est possible que vous pensiez à une situation légèrement différente? Cependant, heureusement, vous vous trompez totalement - essayez-le !! Mettez simplement un UIButton sur un contrôleur de vue. définir évidemment le centre horizontal et vertical. Et puis ajoutez une contrainte de largeur> = 100. cela fonctionne parfaitement dans le dernier iOS.
Fattie
1
(Pour tous ceux qui découvrent la mise en page automatique sur Google ici ........... juste TBC, vous n'avez rien à ajouter du tout, et UIButton (également UILabel, etc.) se redimensionnera automatiquement à la largeur avec les modifications de texte. contraindre correctement la position H / V.)
Fattie
@Fattie démonstration . En particulier, à l'exécution, il se comporte comme ça: github.com/Coeur/autolayoutButton/blob/master/buttonSelfSizing
Cœur

Réponses:

1

Dans Xcode 4.5 et supérieur, cela peut maintenant être fait en utilisant ' Auto-layouting / Constraints '.

Les principaux avantages sont les suivants:

  1. Vous n'avez pas du tout besoin de définir des cadres par programmation!
  2. Si c'est bien fait, vous n'avez pas à vous soucier de réinitialiser les cadres pour les changements d'orientation.
  3. De plus, les changements d'appareil ne doivent pas vous déranger (lire, pas besoin de coder séparément pour différentes tailles d'écran).

Quelques inconvénients:

  1. Non rétrocompatible - fonctionne uniquement pour iOS 6 et supérieur.
  2. Besoin de se familiariser (mais gagnera du temps plus tard).

La chose la plus cool est que nous nous concentrons sur la déclaration d'une intention telle que:

  • Je veux que ces deux boutons aient la même largeur; ou
  • J'ai besoin que cette vue soit centrée verticalement et s'étende jusqu'à un maximum de 10 pts du bord du superview; ou même,
  • Je veux que ce bouton / étiquette soit redimensionné en fonction de l'étiquette qu'il affiche!

Voici un tutoriel simple pour vous familiariser avec la mise en page automatique.

Pour plus de détails .

Cela prend du temps au début, mais il semble que cela en vaudra la peine.

codeburn
la source
3
J'ai failli manquer cette réponse - il faut vraiment beaucoup plus de votes. Avec cette solution, on peut éviter d'incorporer des noms et des tailles de polices ou des appels iOS 7 uniquement. J'ai simplement défini la contrainte du côté sur lequel je voulais que mon UIButton s'étende et c'est tout.
Carl
Presque une solution parfaite, malheureusement cela ne fonctionne pas si vous utilisez titleEdgeInsets: /
Zoltán
130
C'est une mauvaise réponse - elle détaille les mérites de la mise en page automatique elle-même, plutôt que la façon dont la mise en page automatique peut être appliquée dans cette situation pour résoudre le problème du demandeur.
mattsven
4
Je me demandais si cela était possible de le faire par programme dit la réponse ...
Juan Boero
@JuanPabloBoeroAlvarez Les contraintes de mise en page automatique peuvent être spécifiées dans le générateur d'interface et / ou par programme.
Slipp D.Thompson
130

Dans UIKit, il existe des ajouts à la classe NSString pour obtenir à partir d'un objet NSString donné la taille qu'il prendra lors du rendu dans une certaine police.

Docs était . Maintenant, c'est ici sous Obsolète.

Bref, si vous y allez:

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

... vous aurez défini le cadre du bouton sur la hauteur et la largeur de la chaîne que vous rendez.

Vous voudrez probablement expérimenter avec un espace tampon autour de cette CGSize, mais vous commencerez au bon endroit.

Dan Ray
la source
4
On dirait que le rembourrage par défaut d'iOS est 12px, 8px.
Ben Lachman
19
sizeWithFont est obsolète dans iOS 7.0. Utilisez sizeWithAttributes: à la place
carmen_munich
6
Je suggère fortement de ne plus jouer avec les cadres, mais UNIQUEMENT avec des contraintes à la place.
Gil Sand
@GilSand Existe-t-il un moyen d'utiliser le générateur d'interface pour cela?
huggie
Oui, une recherche Google devrait vous donner beaucoup de résultats utiles
Gil Sand
101

La façon de faire cela dans le code est:

[button sizeToFit];

Si vous effectuez un sous-classement et souhaitez ajouter des règles supplémentaires, vous pouvez remplacer:

- (CGSize)sizeThatFits:(CGSize)size;
Daniel Wood
la source
5
sizeToFit fonctionne parfaitement sur les contrôles standard comme UIButton ou UILabel. Si vous le faites sur un UIView personnalisé, vous devrez l'implémenter -(CGSize)sizeThatFits:(CGSize)size.
Cameron Lowell Palmer
4
Cela ne fonctionne que si vous le mettez après avoir défini le texte, sinon il n'y a pas assez d'informations sur sa taille: [button setTitle:@"Your text here" forState:UIControlStateNormal]; [button sizeToFit]; De même, si vous mettez à jour le texte plus tard, n'oubliez pas de le rappeler.
Juan Boero
Assurez-vous que vous ne définissez le texte que de cette façon: [myButton setTitle: @ "my title" forState: UIControlStateNormal]; Pour moi, cela n'a fonctionné que lorsque j'ai répété cette déclaration.
Darius Miliauskas
7
sizeToFit()ne respecte pas les encarts de bord de titre.
kelin
32

Si votre bouton a été créé avec Interface Builder et que vous modifiez le titre dans le code, vous pouvez le faire:

[self.button setTitle:@"Button Title" forState:UIControlStateNormal];
[self.button sizeToFit];
Guptron
la source
1
Et vous pouvez le faire même si votre bouton a été créé avec du code.
Markus
22

Rapide:

La chose importante ici est de savoir quand appellerez-vous le .sizeToFit(), cela devrait être après avoir défini le texte à afficher pour qu'il puisse lire la taille nécessaire, si vous mettez à jour le texte plus tard, appelez-le à nouveau.

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

Pour ce faire à l'aide de la disposition automatique, essayez de définir une contrainte de largeur variable:

Contrainte de largeur

Vous devrez peut-être également ajuster votre Content Hugging Priorityet Content Compression Resistance Prioritypour obtenir les résultats dont vous avez besoin.


UILabel est auto-dimensionnant complètement :

Cet UILabel est simplement paramétré pour être centré sur l'écran (deux contraintes uniquement, horizontale / verticale):

Il change de largeur de manière totalement automatique:

Vous n'avez pas besoin de définir de largeur ou de hauteur - c'est totalement automatique.

entrez la description de l'image ici

entrez la description de l'image ici

Notez que les petits carrés jaunes sont simplement attachés ("espacement" de zéro). Ils se déplacent automatiquement lorsque le UILabel se redimensionne.

L'ajout d'une contrainte "> =" définit une largeur minimale pour le UILabel:

entrez la description de l'image ici

Tad
la source
16

Si vous souhaitez redimensionner le texte par opposition au bouton, vous pouvez utiliser ...

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
la source
3
* ajustsFontSizeToFitWidth
Daniel Bang
8

sizeToFit ne fonctionne pas correctement. au lieu:

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

vous pouvez également ajouter contentInsetau bouton:

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

Hashem Aboonajmi
la source
À partir de la documentation Apple concernant sizeToFit: // appelle sizeThatFits: avec les limites d'affichage actuelles et modifie la taille des limites. Donc, cela fonctionne correctement, il vous suffit de le définir après avoir défini le texte.
Markus
5

Simplement:

  1. Créer UIViewcomme wrapper avec une disposition automatique pour les vues autour.
  2. Mettez UILabeldans cet emballage. Ajoutez des contraintes qui colleront votre étiquette aux bords de l'emballage.
  3. Mettez à l' UIButtonintérieur de votre wrapper, puis ajoutez simplement les mêmes contraintes que celles pour lesquelles vous l'avez fait UILabel.
  4. Profitez de votre bouton autosized avec du texte.
Bartłomiej Semańczyk
la source
1
Cela joue-t-il bien avec l'accessibilité?
jasonszhao
4

Swift 4.2

Dieu merci, cela est résolu. Après avoir défini un texte sur le bouton, vous pouvez récupérer intrinsicContentSize qui est la taille naturelle d'un UIView (le document officiel est ici ). Pour UIButton, vous pouvez l'utiliser comme ci-dessous.

button.intrinsicContentSize.width

Pour votre information, j'ai ajusté la largeur pour qu'elle soit correcte.

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

Simulateur

UIButtons avec intrinsicContentSize

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

nator333
la source
1
Pourriez-vous donner un peu plus de contexte? Quel est le lien entre votre ligne de code et le code de la question? Pourriez-vous également résumer ce que fait le code que vous suggérez? Fournir uniquement le lien comme explication n'est pas le bon format pour ce site (voir meta.stackexchange.com/a/8259/266197 pour des raisons), mais si vous ajoutiez des explications et du contexte, votre réponse deviendrait plus précieuse!
NOhs
3

J'ai quelques besoins supplémentaires liés à ce poste qui sizeToFitne résout pas. J'avais besoin de garder le bouton centré dans son emplacement d'origine, quel que soit le texte. Je ne veux pas non plus que le bouton soit plus grand que sa taille d'origine.

Donc, je dispose le bouton pour sa taille maximale, enregistre cette taille dans le contrôleur et utilise la méthode suivante pour dimensionner le bouton lorsque le texte change:

+ (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
la source
1

Cette classe de bouton avec redimensionnement automatique de la hauteur pour le texte (pour Xamarin mais peut être réécrite pour une autre langue)

[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
la source
1

Pour une raison quelconque, func sizeToFit()ne fonctionne pas pour moi. Ma configuration est que j'utilise un bouton dans un UITableViewCellet que j'utilise la mise en page automatique.

Ce qui a fonctionné pour moi, c'est:

  1. obtenir la contrainte de largeur
  2. obtenir la intrinsicContentSizelargeur, car selon la mise en page automatique de ce document, il n'est pas conscient de la intrinsicContentSize. Définissez la contrainte de largeur sur la intrinsicContentSizelargeur

entrez la description de l'image ici

Voici deux titres du Debug View Hierachry entrez la description de l'image ici

entrez la description de l'image ici

Loozie
la source
0

Pour être honnête, je pense que c'est vraiment dommage qu'il n'y ait pas de simple case à cocher dans le storyboard pour dire que vous voulez redimensionner les boutons pour accueillir le texte. Bien, peu importe.

Voici la solution la plus simple utilisant le storyboard.

  1. Placez UIView et mettez-lui des contraintes. Exemple: entrez la description de l'image ici
  2. Placez UILabel dans UIView. Définissez des contraintes pour l'attacher aux bords de UIView.

  3. Placez votre UIButton dans UIView. Définissez les mêmes contraintes pour l'attacher aux bords de UIView.

  4. Définissez 0 pour le nombre de lignes d'UILabel. entrez la description de l'image ici

  5. Installez les points de vente.

    @IBOutlet var button: UIButton!
    @IBOutlet var textOnTheButton: UILabel!
  1. Obtenez un titre long, long, long.

    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. Sur viewDidLoad, définissez le titre pour UILabel et UIButton.

    override func viewDidLoad() {
        super.viewDidLoad()
        textOnTheButton.text = someTitle
        button.setTitle(someTitle, for: .normal)
        button.titleLabel?.numberOfLines = 0
    }
  1. Exécutez-le pour vous assurer que ce bouton est redimensionné et peut être enfoncé.

entrez la description de l'image ici

Shalugin
la source