79

I've seen posts regarding right alignment but I can't get left-alignment to work. I want the button to take up the width of the screen, with the image on the left and the title/text in the center.

This does not work (at least reliably):

    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);
user1007721
  • 811
  • 1
  • 6
  • 4

27 Answers27

76

This solution works with Swift 3 and respects original content and image edge insets while keeping the title label always centered in the available space, which makes much easier adjusting margins.

It overrides titleRect(forContentRect:) method and returns the correct frame:

@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)
    }
}

The following insets:

enter image description here

Would result in this:

enter image description here


Deprecated previous answer

This works in most scenarios, but some layouts cause layoutSubviews to recursively call itself in an endless loop, so use with caution.

@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)
    }
}

This code would do the same but aligning the icon to the right edge:

@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

The right allignment version uses semanticContentAttribute so it requires iOS 9+.

redent84
  • 18,901
  • 4
  • 62
  • 85
  • I believe frame.width should be frame.size.width – SArnab Jan 19 '17 at 19:33
  • @SArnab `frame.width` is correct https://developer.apple.com/reference/coregraphics/cgrect/1454758-width – redent84 Jan 20 '17 at 09:47
  • `frame.width` is a get only property so we can use it while want to read the frame with :) – MohyG Aug 21 '17 at 06:40
  • 7
    You have to set `contentHorizontalAlignment = .left` to make it work. – Radek Wilczak Sep 24 '18 at 11:35
  • 1
    In the last line: return titleRect.offsetBy(...) I had to change to move the the title rect from parent rect: return contentRect.offsetBy(...) – javi_swift Feb 12 '19 at 12:54
  • And if you'd like to the text to be 'truly centered' in the button frame, ignoring the image, change the dx to: `round((availableWidth - imageSize.width) / 2)` – androidguy Oct 19 '20 at 23:32
52

In my end, I did this using UIEdgeInsetsMake which the left corner is calculated to make it to the center. I am not sure if there's something really you can make the text at aligned at the center but this works for me. Make sure that you set your left corner to your desired position by calculating the width. e.g. 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];

The output looks like,

centered text with image

In 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)
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
toytoy
  • 1,223
  • 1
  • 12
  • 14
  • 8
    The takeaway here for me was to set the content horizontal alignment to Left, rather than the default center. The text itself is still centered within the text area. Your magic number '42' here is the width of the image, easily computed. – Graham Perks Feb 26 '15 at 14:37
  • googlebtn.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: -20.0, bottom: 0.0, right: 0.0); – Abdul Yasin Nov 03 '20 at 13:52
20

For Swift 4.0, here's an extension that works-

extension UIButton {

  func leftImage(image: UIImage, renderMode: UIImage.RenderingMode) {
       self.setImage(image.withRenderingMode(renderMode), for: .normal)
       self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
       self.titleEdgeInsets.left = (self.frame.width/2) - (self.titleLabel?.frame.width ?? 0)
       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 can be .alwaysTemplate or .alwaysOriginal. Also, myButton should be a custom type UIButton.

This extension's leftImage and rightImage can also be used in UIButton in UIBarButtonItem for UINavigationBar(Note: as of iOS 11, the navigation bar follows autolayout so you will need to add width/height constraints to the UIBarButtonItem). For usage on Navigation Bar, make sure you follow the Apple recommended @2x and @3x image sizes (i.e. 50x50, 75x75) and to have better accessibility on iPhone 6, 7 , 8, 6s, 7s, 8s, the Plus variants and iPhone x the width and height of the UIBarButton could be height - 25 and width - 55 (or whatever your app requires, these numbers are some basic numbers that should work for most cases).


UPDATE: In Swift 4.2, UIImageRenderingMode has been renamed to 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
    }
}
GyroCocoa
  • 1,542
  • 16
  • 19
Anjan Biswas
  • 7,746
  • 5
  • 47
  • 77
  • this solution must add below code. let rightInset = (image.size.width / 2) + 10 self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: rightInset) – Deepak Thakur Jun 12 '19 at 06:23
  • 6
    Doesn't work. Title alignment is not centered in the button, which is what the OP wants. – Peter Suwara Mar 16 '20 at 13:51
  • The title's alignment is relative to the image being used, so of course the title won't be centered relative to the buttons width. If you center the label relative to the button width then certain titles may overlap the image depending on the length of the title. – Anjan Biswas Mar 17 '20 at 19:38
12

I've tried almost every solution above and none of them worked as I expected (I had two buttons each with different tittle and with different image width). So I have written my own extension for UIButton, which works flawlessly with different image widths as well as with different titles.

extension UIButton {
    func moveImageLeftTextCenter(imagePadding: CGFloat = 30.0, titlePadding: CGFloat = 0.0, minImageTitleDistance: CGFloat = 10.0){
    guard let imageViewWidth = imageView?.frame.width else{return}
    guard let titleLabelWidth = titleLabel?.intrinsicContentSize.width else{return}
    contentHorizontalAlignment = .left

    let imageLeftInset = imagePadding - imageViewWidth / 2
    var titleLeftInset = (bounds.width - titleLabelWidth) / 2 - imageViewWidth + titlePadding

    if titleLeftInset - imageLeftInset < minImageTitleDistance{
        titleLeftInset = imageLeftInset + minImageTitleDistance
    }

    imageEdgeInsets = UIEdgeInsets(top: 0.0, left: imageLeftInset, bottom: 0.0, right: 0.0)
    titleEdgeInsets = UIEdgeInsets(top: 0.0, left: titleLeftInset, bottom: 0.0, right: 0.0)
    }
}

Usage: myButton.moveImageLeftTextCenter()

Alex
  • 259
  • 2
  • 13
9

As I think, the good solution must be useful for any text, without hardcoded values for insets (it's about the solution provided by toytoy). I use the code similar to this:

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)];

After the string with stringBoundingBox init I also add some strings like this:

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]];
}

it allow to me set the titles longer than available space, by selecting some smaller fonts. I make this because of another way for this:

[buttonView.titleLabel setAdjustsFontSizeToFitWidth:YES];

works not very good, looks like it take the font size for 3-4 points smaller at one step, so some not so long lines become too smaller, than it must to be.

This code allow us to place any text in button title space exactly in center.

Denis Kozhukhov
  • 1,205
  • 8
  • 16
9
[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];

Let me know if it is not working.

My output enter image description here

9

I wrote an UIButton extension.

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)
  }
}

And you could use like this :

yourButton.leftImage(image: yourImage)

Voila !

Maximelc
  • 2,384
  • 1
  • 21
  • 17
8

Create your button and set the text alignment to center, then add:

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];
Danilo
  • 3,257
  • 2
  • 19
  • 24
7

Tried most of the answers with no luck. I was getting a blue image most of the time, or the title was not center. Used code from a few of the answers and wrote this extension, works like a charm.

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)
    }
}

Hope it helps for some!

Matthew Mitchell
  • 514
  • 4
  • 14
5

This works well for me, for several buttons, with different image width and different title length :

Subclass UIButton, and add the following method:

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)
    }

}
Rémy Virin
  • 3,379
  • 23
  • 42
3

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

2) setImageEdgeInsets with negative values, when your button fills the screen, is madness. Reconsider what you're trying to do here?

3) UITextAlignmentCenter is now NSTextAlignmentCenter

4) button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;

buildsucceeded
  • 4,203
  • 4
  • 34
  • 72
3

I wrote an UIButton extension in 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)
    }
}

I would style my button text any way i want, keep it in center alignment. Then I would call this method on my button like -

myButton.setLeftImage("image_name", 30.0)

This would result in my image to be left aligned with some padding from the left border.

Tushar Koul
  • 2,830
  • 3
  • 31
  • 62
  • You have a parameter as 'padding' and a value instantiated in the function called 'padding'. This overrides the parameter function and should be optimized before being used. – Jstngoulet Jul 10 '19 at 22:46
2

I see a lot of solutions here that focus on setting the icon to the left. I think it's way easier to just add a UIImageView, align the left sides of the button and the image and center them together as well. Then you can play a bit with the offset to make it look nice.

No code, all in Interface Builder.

Lucas van Dongen
  • 9,328
  • 7
  • 39
  • 60
  • Yes, but would that image respond to user's touches like change in color? – vedrano Oct 06 '16 at 18:31
  • You can use tint color on all image s but it's not on by default: http://stackoverflow.com/questions/22170688/using-tint-color-on-uiimageview – Lucas van Dongen Oct 06 '16 at 19:21
  • I guess you are right. So, you propose that app should override setSelected() method and maintain image appearance manually (not default behaviour). – vedrano Oct 07 '16 at 09:05
2

One trick is to:

  1. Override titleRectForContentRect to make the button's titleLabel's frame equal to the button's bounds
  2. Set the titleLabel's textAlignment to .Center
  3. override imageRectForContentRect to specify the origin.x of the button's imageView

    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")
        }
    }
    
Eric
  • 16,003
  • 15
  • 87
  • 139
2

I know this answer is kinda late. But I have a very simple solution for folks who want to add an image, using UIImageView, in the left side of an UIButton with centered text. All programmatically

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
    }

}
2

Following extension works for me in 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)
}
sandpat
  • 1,478
  • 12
  • 30
2

My SWIFT 5.2 solution.

You have to subclass UIButton class. No need to change the content horizontal alignment like others did in other answers, keep it to "center" (.center). The title will be automatically centered, while the override of imageRect() will do the trick for the image.

The first thing you have to do is to assign CustomButton class to your button in storyboard's Identity Inspector section. Then, you can switch "alignImageToLeft" to ON or OFF on Attribute Inspector's section (default will be OFF).

class CustomButton : UIButton {

    @IBInspectable var alignImageToLeft : Bool = false

    override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
        if(alignImageToLeft){
            let imageRect = super.imageRect(forContentRect: contentRect)
            let offset = contentRect.minX - imageRect.minX
            return imageRect.offsetBy(dx: offset, dy: 0.0)
        }
        return super.imageRect(forContentRect: contentRect)
    }
}
EmmettBrown88
  • 148
  • 1
  • 2
  • 13
1

You can use below code to Left-align image on UIButton :-

Step 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;

Step 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];

Step 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];

Step 4:

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

thanks,

Kupendiran iOS
  • 217
  • 2
  • 5
1

this code makes your button image to align left, and title label moves to center of button. b is button :)

b.contentHorizontalAlignment = .Left
let left = (b.frameWidth() - b.titleLabel!.frameWidth()) / 2 - CGRectGetMaxX(b.imageView!.frame)
b.titleEdgeInsets = UIEdgeInsetsMake(0, left, 0, 0)
aturan23
  • 4,798
  • 4
  • 28
  • 52
Sergey Sahakyan
  • 723
  • 8
  • 15
1

This solution can be applied using an extension. No need of subclassing.

Warning: this must be called every time the text/image is updated.

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)
vbrittes
  • 200
  • 1
  • 14
1

Swift version:

var button = UIButton()

newGameButton.setTitle("Новая игра", for: .normal)
newGameButton.setImage(UIImage(named: "energi"), for: .normal)
newGameButton.backgroundColor = .blue
newGameButton.imageEdgeInsets.left = -50

enter image description here

aturan23
  • 4,798
  • 4
  • 28
  • 52
1

You may forget about UIEdgeInsets and simply override frames in layoutSubviews() within your UIButton subclass.

To center the titleLable, simply add horizontal offset to the titleLabel frame by the difference between super.bounds.midX and titleLabel.frame.midX

   override func layoutSubviews() {
       super.layoutSubviews()
       
       if let label = titleLabel {
           label.frame = label.frame.offsetBy(dx: super.bounds.midX - 
                label.frame.midX , dy: 0)
       }

       // Available space to the left of the titleLabel is simply:
       let leftAvailableWidth = label.frame.minX

       imageView?.frame = CGRect(x: 0, y: 0, width: <Some width smaller than leftAvailableWidth>, height: super.bound.height)
   }
Ting
  • 51
  • 1
1

@trishcode answer worked for me. I am just using it with an extension of the UIButton.

Here is the code:

extension UIButton {
func centerImageLeft(padding: CGFloat = 30.0){
    let buttonWidth = self.frame.width
    let textWidth = self.titleLabel?.intrinsicContentSize.width ?? 0
    let imageViewWidth = self.imageView?.frame.size.width ?? 0
    let offsetToLeftButtonEdge = buttonWidth - textWidth - imageViewWidth
    let offset = offsetToLeftButtonEdge - padding
    self.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: 0, bottom: 0.0, right: offset)
}
}

You can then call it like this in the viewDidAppear Method:

button.centerImageLeft()
OLLMPH
  • 11
  • 3
0

Swift 5: You can achieve this by using custom class of UIButton.

class CustomButton: UIButton {

    var imageV = UIImageView()
    var titleV = UILabel()

    override func awakeFromNib() {

        self.imageView?.isHidden = true
        self.titleLabel?.isHidden = true

        imageV.frame = CGRect(x: 0, y: 0, width: 40, height: self.bounds.height)
        imageV.contentMode = .scaleAspectFit

        titleV.frame = CGRect(x: 40, y: 0, width: self.bounds.width - 40, height: self.bounds.height)
        titleV.font = self.titleLabel?.font
        titleV.textAlignment = .center

        self.addSubview(imageV)
        self.addSubview(titleV)

        imageV.image = self.imageView?.image; titleV.text = self.titleLabel?.text

        imageV.translatesAutoresizingMaskIntoConstraints = false
        imageV.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
        imageV.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
        imageV.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 0).isActive = true
        imageV.trailingAnchor.constraint(equalTo: titleV.leadingAnchor, constant: 0).isActive = true
        imageV.widthAnchor.constraint(equalToConstant: 40).isActive = true

        titleV.translatesAutoresizingMaskIntoConstraints = false
        titleV.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
        titleV.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
        titleV.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: 0).isActive = true
    }
}

And later you can set image and text like this.

button.imageV.image = myImage
button.titleV.text = myText

Result

enter image description here

ZAFAR007
  • 3,049
  • 1
  • 34
  • 45
0

This works for me with varying button text sizes.

The key for an accurate button text width is to get the intrinsicContentSize. Since I'm using auto layout for the button width, I had to run this code from viewDidAppear instead of viewDidLoad so the button would be drawn already and, therefore, the button frame size would be accurate.

private func calculateAccountButtonImageViewOffset(button: UIButton, padding: CGFloat = 30.0) -> CGFloat {
    let buttonWidth = button.frame.width
    let textWidth = button.titleLabel?.intrinsicContentSize.width ?? 0
    let imageViewWidth = button.imageView?.frame.size.width ?? 0
    let offsetToLeftButtonEdge = buttonWidth - textWidth - imageViewWidth
    return offsetToLeftButtonEdge - padding
}

Usage:

let imageViewOffset = calculateAccountButtonImageViewOffset(button: button)
button.imageEdgeInsets = UIEdgeInsets(top: 0.0, left: 0, bottom: 0.0, right: imageViewOffset)

The button title can be offset as desired with Title Insets on IB: enter image description here

trishcode
  • 3,299
  • 1
  • 18
  • 25
0

SWIFT 5+

Maybe the easiest and best looking variant is to use UIButton.Configuration

private func bottomButtonsConfiguration() -> UIButton.Configuration {
  var config: UIButton.Configuration = .filled()
  config.background.backgroundColor = Asset.Colors.cellsBackground.color
  config.baseForegroundColor = Asset.Colors.textColorOnLight.color
  config.cornerStyle = .small
  config.imagePadding = 14
  config.imagePlacement = .top
  config.imageColorTransformer = UIConfigurationColorTransformer { _ in
    return Asset.Colors.primaryColor.color
  }
  config.titleTextAttributesTransformer = UIConfigurationTextAttributesTransformer { attribute in
    var muttableAttribute = attribute
    muttableAttribute.font = self.theme.font(style: .medium, size: 16)
    return muttableAttribute
  }

  return config
}

Solution for your problem is config.imagePlacement = .top

Expected result img

MIhail
  • 28
  • 5
-2

It might be best to create a custom UIButton subclass to address this issues in a better manner. Apple might be 'resetting' your settings.

Christian
  • 1,714
  • 8
  • 9