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?

Pavel Yakimenko
la source
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);
Divisé
la source
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
Alvivi
la source
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).

Steven Canfield
la source
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;
}
Chris Miles
la source
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

Raymond W
la source
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.

DrAL3X
la source
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.

Luisma
la source
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  
    }
}
ChikabuZ
la source
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);
}
BFar
la source
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.

caliss
la source
1
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
Todd Vanderlin
la source
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];
Pavel Volobuev
la source
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.

Kie
la source
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
        }
    }
}
Pavel Yakimenko
la source
0

Solution monoligne dans Swift:

// iOS 9 and Onwards
button.semanticContentAttribute = .forceRightToLeft
Sunil Targe
la source
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