web-dev-qa-db-fra.com

iPhone UIButton - position de l'image

J'ai un UIButton avec le texte "Explorez l'application" et UIImage (>) Dans Interface Builder cela ressemble à:

[ (>) Explore the app ]

Mais je dois placer cette UIImage APRES le texte:

[ Explore the app (>) ]

Comment puis-je déplacer la UIImage vers la droite?

108
Pavel Yakimenko

Forcer le bouton de droite à gauche n'est pas une option si votre application prend en charge les options 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 des contraintes (testée dans 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.

5
Luisma

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);
159
Split

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

 enter image description here

Ou par programme, en utilisant: 

button.semanticContentAttribute = .ForceRightToLeft
110
Alvivi

Définissez les options imageEdgeInset et titleEdgeInset pour 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 (utilisez ensuite titleEdgeInsets pour déplacer le titre).

83
Steven Canfield

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;
}
54
Chris Miles

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

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

30
Raymond W

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.

11
DrAL3X

En rapide:

override func layoutSubviews()
{
    super.layoutSubviews()

    var imageFrame = self.imageView?.frame;
    var labelFrame = self.titleLabel?.frame;

    let inset: CGFloat = 5

    if var imageFrame = imageFrame
    {
        if var labelFrame = labelFrame
        {
            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
ChikabuZ

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 titre personnalisées définies au préalable (par exemple, dans le storyboard). 

Par exemple, vous voudrez peut-être que l'image ait un rembourrage en haut et en bas du conteneur, tout en la déplaçant tout de même à droite 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);
}
4
BFar
// 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 suit:

[           button label (>)]

Le bouton n'ajuste pas sa largeur en fonction du contexte. Un espace apparaît donc à gauche de l'étiquette. Vous pouvez résoudre ce problème en calculant la largeur du cadre du bouton à partir des propriétés buttonLabelSize.width et buttonImageSize.width.

2
caliss
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
1
Todd Vanderlin

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
Pavel Volobuev

Construire sur les réponses précédentes. Si vous souhaitez avoir une marge entre l'icône et le titre du bouton, le code doit être légèrement modifié pour éviter que le libellé et l'icône ne flottent 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
Kie