iPhone UIButton - position de l'image


116

J'ai un UIButtontexte avec "Explorez l'application" et UIImage(>) Il Interface Builderressemble à:

[ (>) Explore the app ]

Mais je dois placer ceci UIImageAPRÈS le texte:

[ Explore the app (>) ]

Comment puis-je déplacer le UIImagevers la droite?


Vous pouvez également utiliser Interface Builder. Découvrez ma réponse ici: stackoverflow.com/questions/2765024/…
n8tr

1
Utilisez simplement cette sous-classe avec quelques lignes de code: github.com/k06a/RTLButton
k06a

Réponses:


161

Ma solution à cela est assez simple

[button sizeToFit];
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width);

4
Cela fonctionne très bien! Il est difficile de comprendre le concept d'encart de bord. Avez-vous une idée de la raison pour laquelle nous devons définir un encart de bord gauche et droit? Théoriquement, si je déplace le titre vers la gauche et l'image vers la droite, cela suffirait. Pourquoi dois-je régler à la fois à gauche et à droite?
PokerIncome.com

3
LA MEILLEURE SOLUTION QUE J'AI TROUVÉE
Bimawa

2
Cette réponse fonctionne parfaitement. La réponse acceptée est correcte et pointe vers les documents API corrects, mais c'est la solution de copier-coller pour faire ce que l'OP a demandé.
mclaughlinj

Cela devient un peu compliqué lorsque vous commencez à modifier le texte du bouton. La solution sous-classée a mieux fonctionné pour moi dans ce cas.
Brad Goss

Merci de m'avoir montré que vous devez appliquer des largeurs égales aux côtés gauche et droit, je ne comprends pas à 100% comment cela fonctionne (car parfois cela affectera la taille ainsi que l'origine) mais j'ai pu résoudre des problèmes similaires en utilisant cette méthode.
Toby

128

À partir d' iOS 9 , il semble qu'un moyen simple d'y parvenir soit de forcer la sémantique de la vue.

entrez la description de l'image ici

Ou par programmation, en utilisant:

button.semanticContentAttribute = .ForceRightToLeft

4
Même si j'ai aimé votre réponse (+1), je déteste dire que ce n'est peut-être pas la «bonne» façon de le faire, mais c'est l'une des plus simples.
farzadshbfn

@farzadshbfn vous avez raison. J'ai changé ce mot pour «simple», semble plus cohérent.
Alvivi le

@farzadshbfn Pourquoi ne serait-ce pas la "bonne" façon de faire?
Samman Bikram Thapa

3
@SammanBikramThapa IMHO la bonne façon serait de sous-classer l'UIButton et de remplacer layoutSubviews et respecter semanticContentAttributedans notre logique de mise en page, au lieu de se changer semanticContentAttribute. (changement d'approche sémantique, ne fonctionnera pas bien avec l'internationalisation)
farzadshbfn

@farzadshbfn a tout à fait raison. Peu importe que cette réponse marque le plus de points, elle n'est pas correcte - Cela peut casser l'UX pour l'interface de droite à gauche.
Pavel Yakimenko

86

Définissez imageEdgeInsetet titleEdgeInsetpour déplacer les composants dans votre image. Vous pouvez également créer un bouton en utilisant ces graphiques en taille réelle et l'utiliser comme image d'arrière-plan du bouton (puis utiliser titleEdgeInsetspour déplacer le titre).


8
Il est beaucoup moins de code de simplement définir les inserts que d'implémenter une sous-classe. C'est tout l'intérêt des inserts. La manipulation des cadres pour les sous-vues (que vous n'avez pas créées) ressemble plus à un hack.
Kim André Sand

6
@kimsnarf, vraiment? C'est beaucoup moins de travail (et moins de hack) de modifier les encarts chaque fois que vous apportez un changement mineur dans la taille de l'image ou la longueur du titre?
Kirk Woll

54

La réponse de Raymond W est la meilleure ici. Sous-classe UIButton avec layoutSubviews personnalisé. Extrêmement simple à faire, voici une implémentation de layoutSubviews qui a fonctionné pour moi:

- (void)layoutSubviews
{
    // Allow default layout, then adjust image and label positions
    [super layoutSubviews];

    UIImageView *imageView = [self imageView];
    UILabel *label = [self titleLabel];

    CGRect imageFrame = imageView.frame;
    CGRect labelFrame = label.frame;

    labelFrame.origin.x = imageFrame.origin.x;
    imageFrame.origin.x = labelFrame.origin.x + CGRectGetWidth(labelFrame);

    imageView.frame = imageFrame;
    label.frame = labelFrame;
}

2
Cette méthode est meilleure dans le cas où vous auriez besoin de gérer de nombreux boutons, mais je n'ai besoin de changer qu'un seul bouton :)
Pavel Yakimenko

2
Si l'image du bouton est nulle, l'étiquette est mal placée, probablement parce que UIImageView n'est pas inséré (testé sur iOS6.0). Vous ne devriez envisager de modifier les cadres que si imageView.image n'est pas nul.
Scakko

2
Je suggérerais l'amélioration suivante à cette réponse afin que les deux vues restent centrées: 'CGFloat cumulativeWidth = CGRectGetWidth (imageFrame) + CGRectGetWidth (labelFrame) + 10; CGFloat excessiveWidth = CGRectGetWidth (self.bounds) - cumulativeWidth; labelFrame.origin.x = excessWidth / 2; imageFrame.origin.x = CGRectGetMaxX (labelFrame) + 10; '
i-konov

Cela casse sur iOS 7 pour moi. Quelqu'un d'autre? Fonctionne bien sur iOS 8.
rounak

1
Ne supportez pas du tout iOS7 et votre problème sera résolu. Vous ne devriez pas le soutenir de toute façon.
Gil Sand

30

Qu'en est-il du sous UIButton-classement et du remplacement layoutSubviews?

Puis post-traitement des emplacements de self.imageView&self.titleLabel


4
C'est tellement plus facile que tous les autres conseils (comme celui ci-dessus) pour essayer de tout placer correctement en utilisant des inserts réglés à la main.
Steven Kramer le

13

Un autre moyen simple (qui n'est PAS iOS 9 uniquement) consiste à sous-classer UIButton pour remplacer ces deux méthodes

override func titleRectForContentRect(contentRect: CGRect) -> CGRect {
    var rect = super.titleRectForContentRect(contentRect)
    rect.origin.x = 0
    return rect
}

override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
    var rect = super.imageRectForContentRect(contentRect)
    rect.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(rect)
    return rect
}

contentEdgeInsets est déjà pris en compte en utilisant super.


Merci! J'aime beaucoup plus que les réponses qui sous-classent et remplacent layoutSubviews () :)
Joe Susnick

1
À mon humble avis, la meilleure façon de procéder :)
DrPatience

9

Forcer «de droite à gauche» pour le bouton n'est pas une option si votre application prend en charge à la fois «de gauche à droite» et «de droite à gauche».

La solution qui a fonctionné pour moi est une sous-classe qui peut être ajoutée au bouton dans le Storyboard et fonctionne bien avec les contraintes (testé sous iOS 11):

class ButtonWithImageAtEnd: UIButton {

    override func layoutSubviews() {
        super.layoutSubviews()

        if let imageView = imageView, let titleLabel = titleLabel {
            let padding: CGFloat = 15
            imageEdgeInsets = UIEdgeInsets(top: 5, left: titleLabel.frame.size.width+padding, bottom: 5, right: -titleLabel.frame.size.width-padding)
            titleEdgeInsets = UIEdgeInsets(top: 0, left: -imageView.frame.width, bottom: 0, right: imageView.frame.width)
        }

    }

}

Où «padding» serait l'espace entre le titre et l'image.


Bien sûr, .forceRightToLeftc'est une option! Utilisez simplement la valeur opposée ( .forceLeftToRight) if UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft.
manmal

5

Dans Swift:

override func layoutSubviews(){
    super.layoutSubviews()

    let inset: CGFloat = 5

    if var imageFrame = self.imageView?.frame,
       var labelFrame = self.titleLabel?.frame {

       let cumulativeWidth = imageFrame.width + labelFrame.width + inset
       let excessiveWidth = self.bounds.width - cumulativeWidth
       labelFrame.origin.x = excessiveWidth / 2
       imageFrame.origin.x = labelFrame.origin.x + labelFrame.width + inset

       self.imageView?.frame = imageFrame
       self.titleLabel?.frame = labelFrame  
    }
}

4

Construire la réponse par @split ...

La réponse est fantastique, mais elle ignore le fait que le bouton peut avoir des incrustations d'image et de bord de titre personnalisées qui sont définies à l'avance (par exemple dans le storyboard).

Par exemple, vous voudrez peut-être que l'image ait un remplissage du haut et du bas du conteneur, mais déplacez toujours l'image vers le côté droit du bouton.

J'ai étendu le concept avec cette méthode: -

- (void) moveImageToRightSide {
    [self sizeToFit];

    CGFloat titleWidth = self.titleLabel.frame.size.width;
    CGFloat imageWidth = self.imageView.frame.size.width;
    CGFloat gapWidth = self.frame.size.width - titleWidth - imageWidth;
    self.titleEdgeInsets = UIEdgeInsetsMake(self.titleEdgeInsets.top,
                                            -imageWidth + self.titleEdgeInsets.left,
                                            self.titleEdgeInsets.bottom,
                                            imageWidth - self.titleEdgeInsets.right);

    self.imageEdgeInsets = UIEdgeInsetsMake(self.imageEdgeInsets.top,
                                            titleWidth + self.imageEdgeInsets.left + gapWidth,
                                            self.imageEdgeInsets.bottom,
                                            -titleWidth + self.imageEdgeInsets.right - gapWidth);
}

2
// Get the size of the text and image
CGSize buttonLabelSize = [[self.button titleForState:UIControlStateNormal] sizeWithFont:self.button.titleLabel.font];
CGSize buttonImageSize = [[self.button imageForState:UIControlStateNormal] size];

// You can do this line in the xib too:
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

// Adjust Edge Insets according to the above measurement. The +2 adds a little space 
self.button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -(buttonLabelSize.width+2));
self.button.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, buttonImageSize.width+2);

Cela crée un bouton aligné à droite, comme ceci:

[           button label (>)]

Le bouton n'ajuste pas sa largeur en fonction du contexte, de sorte que l'espace apparaîtra à gauche de l'étiquette. Vous pouvez résoudre ce problème en calculant la largeur du cadre du bouton à partir de buttonLabelSize.width et de buttonImageSize.width.


1
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;

0

Cette solution fonctionne sous iOS 7 et supérieur

Juste sous-classe UIButton

@interface UIButton (Image)

- (void)swapTextWithImage;

@end

@implementation UIButton (Image)

- (void)swapTextWithImage {
   const CGFloat kDefaultPadding = 6.0f;
   CGSize buttonSize = [self.titleLabel.text sizeWithAttributes:@{
                                                               NSFontAttributeName:self.titleLabel.font
                                                               }];

   self.titleEdgeInsets = UIEdgeInsetsMake(0, -self.imageView.frame.size.width, 0, self.imageView.frame.size.width);
   self.imageEdgeInsets = UIEdgeInsetsMake(0, buttonSize.width + kDefaultPadding, 0, -buttonSize.width); 
}

@end

Utilisation (quelque part dans votre classe):

[self.myButton setTitle:@"Any text" forState:UIControlStateNormal];
[self.myButton swapTextWithImage];

0

S'appuyant sur les réponses précédentes. Si vous voulez avoir une marge entre l'icône et le titre du bouton, le code doit changer un peu pour empêcher le flottement de l'étiquette et de l'icône au-dessus des limites des boutons de taille intrinsèque.

let margin = CGFloat(4.0)
button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width, 0, button.imageView.frame.size.width);
button.imageEdgeInsets = UIEdgeInsetsMake(0, button.titleLabel.frame.size.width, 0, -button.titleLabel.frame.size.width)
button.contentEdgeInsets = UIEdgeInsetsMake(0, margin, 0, margin)

La dernière ligne de code est importante pour le calcul intrinsèque de la taille du contenu pour la mise en page automatique.


0

Voici ma propre façon de faire la chose, (après environ 10 ans)

  1. Sous-classe d'UIButton (Button, car nous vivons à l'ère Swift)
  2. Mettez une image et une étiquette dans une vue de pile.
class CustomButton: Button {

    var didLayout: Bool = false // The code must be called only once

    override func layoutSubviews() {
        super.layoutSubviews()
        if !didLayout, let imageView = imageView, let titleLabel = titleLabel {
            didLayout = true
            let stack = UIStackView(arrangedSubviews: [titleLabel, imageView])
            addSubview(stack)
            stack.edgesToSuperview() // I use TinyConstraints library. You could handle the constraints directly
            stack.axis = .horizontal
        }
    }
}

0

Solution monoligne dans Swift:

// iOS 9 and Onwards
button.semanticContentAttribute = .forceRightToLeft

Ce n'est pas bon car cela cassera la mise en page pour tous les utilisateurs de RTL. Vous devrez forcer de gauche à droite pour eux. Vous feriez mieux d'essayer une autre solution à partir d'ici.
Pavel Yakimenko le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.