web-dev-qa-db-fra.com

Aligner à gauche l'image et centrer le texte sur UIButton

J'ai vu des articles sur l'alignement à droite, mais je ne parviens pas à obtenir l'alignement à gauche. Je veux que le bouton occupe la largeur de l'écran, avec l'image à gauche et le titre/texte au centre.

Cela ne fonctionne pas (du moins de manière fiable):

    button.titleLabel.textAlignment = UITextAlignmentCenter;
    [button setImageEdgeInsets:UIEdgeInsetsMake(0, -60.0, 0, 0)];
    button.frame = CGRectMake((self.view.frame.size.width - w ) / 2, self.view.frame.size.height - 140.0,  self.view.frame.size.width - 10.0, 40.0);
48
user1007721

Cette solution fonctionne avec Swift 3 et respecte le contenu d'origine et les incrustations d'image tout en maintenant l'étiquette du titre toujours centrée sur l'espace disponible, ce qui facilite beaucoup l'ajustement des marges.

Il écrase la méthode titleRect(forContentRect:) et renvoie le cadre correct:

@IBDesignable
class LeftAlignedIconButton: UIButton {
    override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
        let titleRect = super.titleRect(forContentRect: contentRect)
        let imageSize = currentImage?.size ?? .zero
        let availableWidth = contentRect.width - imageEdgeInsets.right - imageSize.width - titleRect.width
        return titleRect.offsetBy(dx: round(availableWidth / 2), dy: 0)
    }
}

Les encarts suivants:

 enter image description here

Aboutirait à ceci:

 enter image description here


Obsolète réponse précédente

Cela fonctionne dans la plupart des scénarios, mais certaines mises en forme amènent layoutSubviews à s’appeler de manière récursive dans une boucle infinie, donc use avec prudence.

@IBDesignable
class LeftAlignedIconButton: UIButton {
    override func layoutSubviews() {
        super.layoutSubviews()
        contentHorizontalAlignment = .left
        let availableSpace = UIEdgeInsetsInsetRect(bounds, contentEdgeInsets)
        let availableWidth = availableSpace.width - imageEdgeInsets.right - (imageView?.frame.width ?? 0) - (titleLabel?.frame.width ?? 0)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: availableWidth / 2, bottom: 0, right: 0)
    }
}

Ce code ferait la même chose, mais en alignant l'icône sur le bord droit:

@IBDesignable
class RightAlignedIconButton: UIButton {
    override func layoutSubviews() {
        super.layoutSubviews()
        semanticContentAttribute = .forceRightToLeft
        contentHorizontalAlignment = .right
        let availableSpace = UIEdgeInsetsInsetRect(bounds, contentEdgeInsets)
        let availableWidth = availableSpace.width - imageEdgeInsets.left - (imageView?.frame.width ?? 0) - (titleLabel?.frame.width ?? 0)
        titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: availableWidth / 2)
    }
}

 enter image description here

La bonne version de l'allocation utilise semanticContentAttribute et nécessite donc iOS 9+.

45
redent84

De mon côté, je l'ai fait en utilisant UIEdgeInsetsMake dont le coin gauche est calculé pour le rendre au centre. Je ne suis pas sûr qu'il y ait quelque chose de vraiment qui puisse aligner le texte au centre mais cela fonctionne pour moi. Assurez-vous que vous réglez le coin gauche à la position souhaitée en calculant la largeur. par exemple. UIEdgeInsetsMake(0.0f, 42.0f, 0.0f, 0.0f)

UIButton *scanBarCodeButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
scanBarCodeButton.frame = CGRectMake(center, 10.0f, fieldWidth, 40.0f);
[scanBarCodeButton setImage:[UIImage imageNamed:@"BarCodeIcon.png"] forState:UIControlStateNormal];
[scanBarCodeButton setTitle:@"Scan the Barcode" forState:UIControlStateNormal];
scanBarCodeButton.titleEdgeInsets = UIEdgeInsetsMake(0.0f, 42.0f, 0.0f, 0.0f);
[scanBarCodeButton setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];
[scanBarCodeButton addTarget:self action:@selector(scanBarCode:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:scanBarCodeButton];

La sortie ressemble à,

texte centré avec l'image http://i43.tinypic.com/33mbxxi.png

En Swift :

var scanBarCodeButton: UIButton = UIButton(type: .roundedRect)
scanBarCodeButton.frame = CGRectMake(center, 10.0, fieldWidth, 40.0)
scanBarCodeButton.setImage(UIImage(named: "BarCodeIcon.png"), for: UIControlStateNormal)
scanBarCodeButton.setTitle("Scan the Barcode", for: UIControlStateNormal)
scanBarCodeButton.titleEdgeInsets = UIEdgeInsetsMake(0.0, 42.0, 0.0, 0.0)
scanBarCodeButton.contentHorizontalAlignment = .left
scanBarCodeButton.addTarget(self, action: "scanBarCode:", for: UIControlEventTouchUpInside)
self.view.addSubview(scanBarCodeButton)
44
toytoy

Créez votre bouton et définissez l'alignement du texte au centre, puis ajoutez:

UIImage *image = [UIImage imageNamed:@"..."];
CGSize imageSize = image.size;
CGFloat offsetY = floor((self.layer.bounds.size.height - imageSize.height) / 2.0);

CALayer *imageLayer = [CALayer layer];
imageLayer.contents = (__bridge id) image.CGImage;
imageLayer.contentsGravity = kCAGravityBottom;
imageLayer.contentsScale = [UIScreen mainScreen].scale;
imageLayer.frame = CGRectMake(offsetY, offsetY, imageSize.width, imageSize.height);
[self.layer addSublayer:imageLayer];
7
Danilo

Comme je le pense, la bonne solution doit être utile pour tout texte, sans valeurs codées en dur pour les incrustations (il s'agit de la solution fournie par toytoy). J'utilise le code similaire à ceci:

NSString *sometitle = @"blabla button title";
NSString *someimage = @"blablaimage";
UIImage *image = [[UIImage imageNamed:someimage];
int buttonWidth = A;
int buttonHeight = B;
int imageWidth =image.size.width;
int imageHeight = image.size.height;
int titleWidth = buttonWidth - imageWidth;

// create button and set image and title
buttonView = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, buttonWidth, buttonHeight)];
[buttonView setImage:image forState:UIControlStateNormal];
[buttonView setTitle:sometitle forState:UIControlStateNormal];

// make button all content to be left aligned
[buttonView setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];

// set title font 
[buttonView.titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:14]];

// calculate font text width with selected font
CGSize stringBoundingBox = [number sizeWithFont:[buttonView.titleLabel font]];

// make title inset with some value from left 
// it place the title right on the center of space for the title
int titleLeft = (titleWidth - stringBoundingBox.width) / 2;
[buttonView setTitleEdgeInsets:UIEdgeInsetsMake(0, titleLeft, 0, 0)];

Après la chaîne avec stringBoundingBox init, j'ajoute également certaines chaînes comme ceci:

if (stringBoundingBox.width > titleWidth)
{
    [_backgroundView.titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:13]];
    stringBoundingBox = [number sizeWithFont:[_backgroundView.titleLabel font]];
}
if (stringBoundingBox.width > titleWidth)
{
    [_backgroundView.titleLabel setFont:[UIFont fontWithName:@"HelveticaNeue" size:12]];
    stringBoundingBox = [number sizeWithFont:[_backgroundView.titleLabel font]];
}

cela me permet de définir des titres plus longs que l'espace disponible, en sélectionnant des polices plus petites. Je fais cela à cause d'une autre façon pour cela:

[buttonView.titleLabel setAdjustsFontSizeToFitWidth:YES];

fonctionne pas très bien, on dirait qu'il faut la taille de la police pour 3-4 points plus petit à la fois, de sorte que certaines lignes pas si longues deviennent trop petites, que cela doit être.

Ce code nous permet de placer n'importe quel texte dans l'espace de titre du bouton exactement au centre.

7
Denis Kozhukhov

Pour Swift 4.0, voici une extension qui fonctionne.

extension UIButton {
    func leftImage(image: UIImage, renderMode: UIImageRenderingMode) {
        self.setImage(image.withRenderingMode(renderMode), for: .normal)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: image.size.width / 2)
        self.contentHorizontalAlignment = .left
        self.imageView?.contentMode = .scaleAspectFit
    }

    func rightImage(image: UIImage, renderMode: UIImageRenderingMode){
        self.setImage(image.withRenderingMode(renderMode), for: .normal)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left:image.size.width / 2, bottom: 0, right: 0)
        self.contentHorizontalAlignment = .right
        self.imageView?.contentMode = .scaleAspectFit
    }
}

Usage:

myButton.rightImage(image: UIImage(named: "image_name")!, renderMode: .alwaysOriginal)
myButton.leftImage(image: UIImage(named: "image_name")!, renderMode: .alwaysOriginal)

renderMode peut être .alwaysTemplate ou .alwaysOriginal. De plus, myButton devrait être un custom type UIButton.

leftImage et rightImage de cette extension peuvent également être utilisés dans UIButton dans UIBarButtonItem pour UINavigationBar (Remarque: à partir de iOS 11, la barre de navigation suit l'autolayout. Vous devrez donc ajouter des contraintes de largeur/hauteur à UIBarButtonItem. Pour une utilisation sur la barre de navigation, veillez à respecter les tailles d'image recommandées par Apple @ 2x et @ 3x (c'est-à-dire 50x50, 75x75) et une meilleure accessibilité sur iPhone 6, 7, 8, 6, 7, 8, 8, les versions Plus et iPhone. x la largeur et la hauteur de la variable UIBarButton pourraient être hauteur - 25 et largeur - 55 (ou ce que votre application requiert, ces chiffres sont quelques chiffres de base qui devraient fonctionner dans la plupart des cas).


UPDATE: Dans Swift 4.2, UIImageRenderingMode a été renommé en UIImage.RenderingMode

extension UIButton {
    func leftImage(image: UIImage, renderMode: UIImage.RenderingMode) {
        self.setImage(image.withRenderingMode(renderMode), for: .normal)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: image.size.width / 2)
        self.contentHorizontalAlignment = .left
        self.imageView?.contentMode = .scaleAspectFit
    }

    func rightImage(image: UIImage, renderMode: UIImage.RenderingMode){
        self.setImage(image.withRenderingMode(renderMode), for: .normal)
        self.imageEdgeInsets = UIEdgeInsets(top: 0, left:image.size.width / 2, bottom: 0, right: 0)
        self.contentHorizontalAlignment = .right
        self.imageView?.contentMode = .scaleAspectFit
    }
}
6
Annjawn
[self.button setImage:[UIImage imageNamed:@"image.png"] forState:UIControlStateNormal];

[self.button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, self.button.center.x/2 , 0.0, 0.0)];

[self.button setContentHorizontalAlignment:UIControlContentHorizontalAlignmentLeft];

Faites-moi savoir si cela ne fonctionne pas.

Ma sortie  enter image description here

6
Deepak Kumar Sahu

J'ai écrit une extension UIButton.

extension UIButton {

  /// Add image on left view
  func leftImage(image: UIImage) {
    self.setImage(image, for: .normal)
    self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: image.size.width)
  }
}

Et vous pourriez utiliser comme ceci:

yourButton.leftImage(image: yourImage)

Voila!

5
Maximelc

Cela fonctionne bien pour moi, pour plusieurs boutons, avec une largeur d'image et une longueur de titre différentes: 

Sous-classe UIButton et ajoutez la méthode suivante:

override func layoutSubviews() {
    super.layoutSubviews()

    if let image = imageView?.image {

        let margin = 30 - image.size.width / 2
        let titleRect = titleRectForContentRect(bounds)
        let titleOffset = (bounds.width - titleRect.width - image.size.width - margin) / 2


        contentHorizontalAlignment = UIControlContentHorizontalAlignment.Left
            imageEdgeInsets = UIEdgeInsetsMake(0, margin, 0, 0)
            titleEdgeInsets = UIEdgeInsetsMake(0, (bounds.width - titleRect.width -  image.size.width - margin) / 2, 0, 0)
    }

}
4
Rémy Virin

Je sais que cette réponse est un peu tardive ... Mais j'ai une solution très simple pour ceux qui veulent ajouter une image, en utilisant UIImageView, dans la partie gauche d'un bouton UIB avec un texte centré. Tous par programmation

class RandomVC: UIViewController{

    var imageDemo: UIImageView = {
    let img = UIImageView()
    img.translatesAutoresizingMaskIntoConstraints = false
    img.image = UIImage(named: "someImgFromAssets")
    return img
    }()

    lazy var someButton: UIButton = { //lazy var: so button can have access to self class
    let button = UIButton(type: .system)
    button.setTitle("This is your title", for: UIControlState.normal)
    button.translatesAutoresizingMaskIntoConstraints = false 
    button.setTitleColor(UIColor.white, for: UIControlState.normal)
    button.addTarget(self, action: #selector(handleButtonClick), for: .touchUpInside) //make sure you write function "handleButtonClick" inside your class
    button.titleLabel?.textAlignment = .center
    return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        view.addSubView(someButton)
        someButton.addSubview(imageDemo)
        imageDemo.centerYAnchor.constraint(equalTo: someButton.centerYAnchor).isActive = true
        imageDemo.leftAnchor.constraint(equalTo: someButton.leftAnchor, constant: 10).isActive = true
        imageDemo.heightAnchor.constraint(equalToConstant: 25).isActive = true
        imageDemo.widthAnchor.constraint(equalToConstant: 25).isActive = true
    }

}
1

Je vois beaucoup de solutions ici qui mettent l’accent sur le réglage de l’icône à gauche. Je pense qu'il est beaucoup plus facile d'ajouter simplement un UIImageView, d'aligner les côtés gauche du bouton et de l'image et de les centrer ensemble. Ensuite, vous pouvez jouer un peu avec le décalage pour le rendre agréable.

Aucun code, tout dans Interface Builder.

1
Departamento B

1) button.frame = self.view.bounds;

2) setImageEdgeInsets avec des valeurs négatives, lorsque votre bouton remplit l'écran, est de la folie. Reconsidérer ce que vous essayez de faire ici?

3) UITextAlignmentCenter est maintenant NSTextAlignmentCenter

4) button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;

1
buildsucceeded

J'ai écrit une extension UIButton dans Swift - 

extension UIButton {

func setLeftImage(imageName:String, padding:CGFloat) {
        //Set left image
        let image = UIImage(named: imageName)
        self.setImage(image, forState: .Normal)

        //Calculate and set image inset to keep it left aligned
        let imageWidth = image?.size.width
        let textWidth = self.titleLabel?.intrinsicContentSize().width
        let buttonWidth = CGRectGetWidth(self.bounds)

        let padding:CGFloat = 30.0
        let rightInset = buttonWidth - imageWidth!  - textWidth! - padding

        self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: rightInset)
    }
}

Je voudrais styler le texte de mon bouton comme je le veux, le garder dans l'alignement du centre ..__ puis appeler cette méthode sur mon bouton comme -

myButton.setLeftImage("image_name", 30.0)

Cela aurait pour effet d'aligner à gauche mon image avec un remplissage du bord gauche.

1
Tushar Koul

ce code permet d'aligner à gauche l'image du bouton et le titre se déplace au centre du bouton. b est le bouton :)

            b.contentHorizontalAlignment = .Left
        let left = (b.frameWidth() - b.titleLabel!.frameWidth()) / 2 - CGRectGetMaxX(b.imageView!.frame)
        b.titleEdgeInsets = UIEdgeInsetsMake(0, left, 0, 0)
0
Sergey Sahakyan

Vous pouvez utiliser le code ci-dessous pour aligner à gauche l'image sur UIButton: -

Étape 1:

//create your UIButton
UIButton *post_bNeeds_BTN= [UIButton buttonWithType:UIButtonTypeRoundedRect];
post_bNeeds_BTN.frame= CGRectMake(160 ,5 ,150 ,40);
[post_bNeeds_BTN addTarget:self action:@selector(post_Buying_Needs_BTN_Pressed:) forControlEvents:UIControlEventTouchUpInside];
[post_bNeeds_BTN setBackgroundColor:[UIColor colorWithRed:243/255.0 green:131/255.0 blue:26/255.0 alpha:1.0f]];//230
[self.view addSubview:post_bNeeds_BTN];
post_bNeeds_BTN.autoresizingMask= UIViewAutoresizingFlexibleWidth;

Étape 2:

//Add UIImageView on right side the button
UIImageView *bNeedsIVw= [[UIImageView alloc] initWithFrame:CGRectMake(5,5,30,30)];
bNeedsIVw.image= [UIImage imageNamed:@"postRequir_30.png"];
bNeedsIVw.image= [bNeedsIVw.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
bNeedsIVw.tintColor= [UIColor whiteColor];
[post_bNeeds_BTN addSubview:bNeedsIVw];

Étape 3:

//also set UILable on UIButton
UILabel *bNeedsLbl= [[UILabel alloc] initWithFrame:CGRectMake(40 ,0 ,post_Prod_BTN.frame.size.width-40 ,post_Prod_BTN.frame.size.height)];
bNeedsLbl.text= @"Requirements";
bNeedsLbl.font= [UIFont systemFontOfSize:16];
bNeedsLbl.textColor= [UIColor whiteColor];
bNeedsLbl.textAlignment= NSTextAlignmentLeft;
[post_bNeeds_BTN addSubview:bNeedsLbl];

Étape 4:

-(void)post_Buying_Needs_BTN_Pressed:(id)sender{
    //write your code action here,,
}

merci,

0
Kupendiran iOS

J'ai essayé presque toutes les solutions ci-dessus et aucune d'entre elles n'a fonctionné comme prévu (j'avais deux boutons, chacun avec un titre différent et une largeur d'image différente). J'ai donc écrit ma propre extension pour UIButton, qui fonctionne parfaitement avec différentes largeurs d'image et différents titres. 

extension UIButton {
    func moveImageLeftTextCenter(imagePadding: CGFloat = 30.0){
        guard let imageViewWidth = self.imageView?.frame.width else{return}
        guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
        self.contentHorizontalAlignment = .left
        imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imagePadding - imageViewWidth / 2, bottom: 0.0, right: 0.0)
        titleEdgeInsets = UIEdgeInsets(top: 0.0, left: (bounds.width - titleLabelWidth) / 2 - imageViewWidth, bottom: 0.0, right: 0.0)
    }
}

Usage: myButton.moveImageLeftTextCenter()

0
Alex

Une astuce consiste à:

  1. Remplacez titleRectForContentRect pour que le bouton titleLabel soit frame égal à ses limites
  2. Définissez titleLabel 's textAlignment sur .Center
  3. remplace imageRectForContentRect pour spécifier le Origin.x de la imageView du bouton

    import UIKit
    
    class ButtonWithLeftAlignedImageAndCenteredText: UIButton {
    
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            titleLabel?.textAlignment = .Center
        }
    
        override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
            var imageFrame = super.imageRectForContentRect(contentRect)
            imageFrame.Origin.x = 20 //offset from left Edge
            return imageFrame
        }
    
        override func titleRectForContentRect(contentRect:CGRect) -> CGRect {
            var titleFrame = super.titleRectForContentRect(contentRect)
            titleFrame = self.bounds
            return titleFrame
        }
    
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
0
Eric

L'extension suivante fonctionne pour moi dans Swift 4.2

func leftImage(image: UIImage, padding: CGFloat, renderMode: UIImage.RenderingMode) {
    self.setImage(image.withRenderingMode(renderMode), for: .normal)
    contentHorizontalAlignment = .left
    let availableSpace = bounds.inset(by: contentEdgeInsets)
    let availableWidth = availableSpace.width - imageEdgeInsets.right - (imageView?.frame.width ?? 0) - (titleLabel?.frame.width ?? 0)
    titleEdgeInsets = UIEdgeInsets(top: 0, left: availableWidth / 2, bottom: 0, right: 0)
    imageEdgeInsets = UIEdgeInsets(top: 0, left: padding, bottom: 0, right: 0)
}

func rightImage(image: UIImage, padding: CGFloat, renderMode: UIImage.RenderingMode){
    self.setImage(image.withRenderingMode(renderMode), for: .normal)
    semanticContentAttribute = .forceRightToLeft
    contentHorizontalAlignment = .right
    let availableSpace = bounds.inset(by: contentEdgeInsets)
    let availableWidth = availableSpace.width - imageEdgeInsets.left - (imageView?.frame.width ?? 0) - (titleLabel?.frame.width ?? 0)
    titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: availableWidth / 2)
    imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: padding)
}
0
Josh

J'ai essayé la plupart des réponses sans succès. J'obtenais une image bleue la plupart du temps, ou le titre n'était pas au centre. Utilisé le code de quelques-unes des réponses et écrit cette extension, fonctionne comme un charme.

Swift 4.2:

import UIKit

extension UIButton {
    func moveImageLeftTextCenter(image : UIImage, imagePadding: CGFloat, renderingMode: UIImage.RenderingMode){
        self.setImage(image.withRenderingMode(renderingMode), for: .normal)
        guard let imageViewWidth = self.imageView?.frame.width else{return}
        guard let titleLabelWidth = self.titleLabel?.intrinsicContentSize.width else{return}
        self.contentHorizontalAlignment = .left
        let imageLeft = imagePadding - imageViewWidth / 2
        let titleLeft = (bounds.width - titleLabelWidth) / 2 - imageViewWidth
        imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imageLeft, bottom: 0.0, right: 0.0)
        titleEdgeInsets = UIEdgeInsets(top: 0.0, left: titleLeft , bottom: 0.0, right: 0.0)
    }
}

J'espère que ça aide pour certains!

0
Matthew Mitchell

Cette solution peut être appliquée à l'aide d'une extension. Pas besoin de sous-classe.

Attention: ceci doit être appelé à chaque mise à jour du texte/image.

let width = frame.width
guard let imageWidth = imageView?.frame.width, let labelWidth = titleLabel?.frame.width else {
      return
}
titleEdgeInsets = UIEdgeInsets(top: 0, left: (width - labelWidth)/2.0 - imageWidth, bottom: 0, right: 0)
0
vbrittes