227 votes

Autolayout - la taille intrinsèque de UIButton n'inclut pas les inserts de titre

Si j'ai un UIButton disposé à l'aide d'autolayout, sa taille s'ajuste bien à son contenu.

Si je définis une image comme button.image la taille intrinsèque semble à nouveau expliquer ce phénomène.

Cependant, si je modifie le titleEdgeInsets du bouton, la mise en page n'en tient pas compte et tronque le titre du bouton.

Comment puis-je m'assurer que la largeur intrinsèque du bouton tient compte de l'encastrement ?

enter image description here

Editar:

J'utilise ce qui suit :

[self.backButton setTitleEdgeInsets:UIEdgeInsetsMake(0, 5, 0, 0)];

L'objectif est d'ajouter une certaine séparation entre l'image et le texte.

3 votes

Avez-vous déposé ce dossier comme un radar ? Il semble bien qu'il s'agisse d'un bogue dans les calculs de la taille intrinsèque de l'UIButton.

1 votes

J'étais prêt à déposer un radar, mais cela semble en fait être un comportement attendu. Ceci est documenté sur *EdgeInsets de UIButton propriétés : " Les inserts que vous spécifiez sont appliqués au rectangle de titre après que ce rectangle a été dimensionné pour s'adapter au texte du bouton. Ainsi, des valeurs d'insertion positives peuvent effectivement couper le texte du titre. [...] Le bouton n'utilise pas cette propriété pour déterminer intrinsicContentSize et sizeThatFits :."

8 votes

@GuillaumeAlgis Je dirais que bien que ce soit un comportement déclaré, c'est no pas du tout ce à quoi on pourrait s'attendre en utilisant autolayout. J'ai signalé un bogue et j'encourage les autres à en signaler un également.

233voto

n.Drake Points 2376

Vous pouvez faire en sorte que cela fonctionne dans Interface Builder (sans écrire de code), en utilisant une combinaison d'inserts de titre et de contenu négatifs et positifs.

enter image description here

Mise à jour : Xcode 7 a un bug où vous ne pouvez pas entrer des valeurs négatives dans le Right Inset, mais vous pouvez utiliser la commande pas à pas située à côté pour diminuer la valeur. (Merci Stuart)

Cela ajoutera 8 pt d'espacement entre l'image et le titre et augmentera la largeur intrinsèque du bouton de la même quantité. Comme ceci :

enter image description here

2 votes

Il utilise contentEdgeInsets (qui n'est pas bogué) pour permettre à autolayout d'augmenter la largeur des boutons. Et déplacer l'étiquette vers l'espace vide à droite. Une façon intelligente de contourner le bogue de l'insertion du bord du titre.

7 votes

Cette astuce ne fonctionne plus. Le constructeur d'interface n'accepte plus les valeurs négatives dans le champ Right champ.

1 votes

@JorisMans c'est exact. Vous pouvez modifier manuellement le storyboard comme solution de rechange.

205voto

jaredsinclair Points 6858

Vous pouvez résoudre ce problème sans avoir à surcharger des méthodes ou à définir une contrainte de largeur arbitraire. Vous pouvez le faire dans Interface Builder comme suit.

  • La largeur intrinsèque du bouton est dérivée de la largeur du titre, de la largeur de l'icône, de la gauche et de la droite. contenu inserts de bord.

  • Si un bouton comporte à la fois une image et du texte, ils sont centrés en tant que groupe, sans remplissage entre eux.

  • Si vous ajoutez un encart de contenu à gauche, il est calculé par rapport au texte, et non par rapport au texte + icône.

  • Si vous définissez une insertion négative de l'image à gauche, l'image est tirée vers la gauche mais la largeur globale du bouton n'est pas affectée.

  • Si vous définissez une insertion négative de l'image à gauche, la mise en page réelle utilise la moitié de cette valeur. Ainsi, pour obtenir une insertion gauche de -20 points, vous devez utiliser une valeur d'insertion gauche de -40 points dans Interface Builder.

Il faut donc prévoir un encart de contenu de gauche suffisamment grand pour créer un espace pour l'encart de gauche souhaité. et le remplissage intérieur entre l'icône et le texte, puis décalez l'icône vers la gauche en doublant la quantité de remplissage que vous souhaitez entre l'icône et le texte. Le résultat est un bouton avec des insertions de contenu égales à gauche et à droite, et une paire de texte et d'icône centrée en tant que groupe, avec une quantité spécifique de remplissage entre eux.

Quelques exemples de valeurs :

// Produces a button with the layout:
// |-20-icon-10-text-20-|
// AutoLayout intrinsic width works as you'd desire.
button.contentEdgeInsets = UIEdgeInsetsMake(10, 30, 10, 20)
button.imageEdgeInsets = UIEdgeInsetsMake(0, -20, 0, 0)

0 votes

Pourquoi la mise en page actuelle utilise la moitié de la valeur négative de l'encart de gauche ? J'ai rencontré le même problème !

1 votes

C'est bien qu'il y ait une solution de contournement, mais j'espère que cela ne sera pas utilisé pour justifier le comportement bizarre des UIButton .

102voto

Maarten Points 921

Pourquoi ne pas remplacer le intrinsicContentSize sur UIView ? Par exemple :

- (CGSize) intrinsicContentSize
{
    CGSize s = [super intrinsicContentSize];

    return CGSizeMake(s.width + self.titleEdgeInsets.left + self.titleEdgeInsets.right,
                      s.height + self.titleEdgeInsets.top + self.titleEdgeInsets.bottom);
}

Cela devrait indiquer au système de mise en page automatique qu'il doit augmenter la taille du bouton pour tenir compte des encarts et afficher le texte complet. Je ne suis pas sur mon propre ordinateur, donc je n'ai pas testé cela.

1 votes

Les boutons ne devraient pas être remplacés, pour autant que je sache. Le problème est que chaque type de bouton est implémenté par une sous-classe différente.

2 votes

intrinsicContentSize est une méthode de UIView, et non de UIButton, de sorte que vous n'avez pas à intervenir dans les méthodes de UIButton. Apple ne pense pas que ce soit un problème : "Le fait de surcharger cette méthode permet à une vue personnalisée de communiquer au système de mise en page la taille qu'elle souhaite avoir en fonction de son contenu." Et le PO n'a rien dit à propos de différents boutons, juste un seul.

1 votes

Cette solution fonctionne et c'est celle que j'ai choisie. intrinsicContentSize est en effet une méthode sur UIView et UIButton est une sous-classe de UIView, donc bien sûr vous pouvez surcharger cette méthode ; rien dans la documentation d'Apple ne dit que vous ne devriez pas le faire. Il suffit de créer une sous-classe de UIButton en utilisant la méthode surchargée de Maarten et de changer votre UIButton dans Interface Builder pour qu'il soit du type YourUIButtonSubclass et cela fonctionnera parfaitement.

95voto

rdelmar Points 53270

Vous n'avez pas précisé comment vous définissez les inserts, je suppose donc que vous utilisez titleEdgeInsets car je vois le même effet que vous. Si j'utilise contentEdgeInsets à la place, cela fonctionne correctement.

- (IBAction)ChangeTitle:(UIButton *)sender {
    self.button.contentEdgeInsets = UIEdgeInsetsMake(0,20,0,20);
    [self.button setTitle:@"Long Long Title" forState:UIControlStateNormal];
}

0 votes

J'utilise en effet titleEdgeInsets. J'ai besoin d'éloigner le titre de l'image, et non l'image du bord du bouton. Peut-être devrais-je simplement utiliser une image avec un peu de rembourrage ? Cela semble un peu compliqué.

0 votes

Cela fonctionne parfaitement en combinaison avec autolayout, merci !

3 votes

C'est la meilleure solution, car elle fait exactement ce que vous voulez sans toucher à intrinsicContentSize.

25voto

pegpeg Points 19

Et pour Swift a travaillé ceci :

extension UIButton {
    override open var intrinsicContentSize: CGSize {
        let intrinsicContentSize = super.intrinsicContentSize

        let adjustedWidth = intrinsicContentSize.width + titleEdgeInsets.left + titleEdgeInsets.right
        let adjustedHeight = intrinsicContentSize.height + titleEdgeInsets.top + titleEdgeInsets.bottom

        return CGSize(width: adjustedWidth, height: adjustedHeight)
    }
}

Love U Swift

2 votes

Même si vous n'êtes pas censé le faire, il est préférable d'utiliser une sous-classe dans ce cas parce que la documentation d'Apple indique explicitement que la taille intrinsèque n'inclut pas titleEdgeInsets dans son calcul et qu'en utilisant une extension, vous violez non seulement les attentes d'Apple mais aussi celles de tous les autres développeurs qui lisent la documentation.

1 votes

La surcharge à l'intérieur d'une extension n'est pas prise en charge et entraîne un comportement d'exécution non défini. voir : stackoverflow.com/a/38274660/4175475

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X