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?
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.
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);
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).
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;
}
Qu'en est-il du sous-classement UIButton
et du remplacement de layoutSubviews
?
Puis post-traitement des emplacements de self.imageView
& self.titleLabel
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.
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
}
}
}
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);
}
// 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.
button.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentRight;
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];
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.