user1007721
user1007721

Reputation: 811

Left-align image and center text on UIButton

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

Upvotes: 79

Views: 108335

Answers (27)

MIhail
MIhail

Reputation: 34

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

Upvotes: -1

OLLMPH
OLLMPH

Reputation: 11

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

Upvotes: 1

trishcode
trishcode

Reputation: 3549

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

Upvotes: 0

Anjan Biswas
Anjan Biswas

Reputation: 7932

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

Upvotes: 19

Ting
Ting

Reputation: 71

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

Upvotes: 1

Sergey Sahakyan
Sergey Sahakyan

Reputation: 739

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)

Upvotes: 1

aturan23
aturan23

Reputation: 5410

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

Upvotes: 2

EmmettBrown88
EmmettBrown88

Reputation: 168

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

Upvotes: 2

Alex
Alex

Reputation: 269

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

Upvotes: 12

Zafar Ahmad
Zafar Ahmad

Reputation: 3219

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

Upvotes: 0

toytoy
toytoy

Reputation: 1251

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)

Upvotes: 52

Matthew Mitchell
Matthew Mitchell

Reputation: 514

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!

Upvotes: 7

sandpat
sandpat

Reputation: 1485

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

Upvotes: 2

vbrittes
vbrittes

Reputation: 200

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)

Upvotes: 1

redent84
redent84

Reputation: 19249

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+.

Upvotes: 76

Maximelc
Maximelc

Reputation: 2414

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 !

Upvotes: 9

Manish Manandhar
Manish Manandhar

Reputation: 41

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
    }

}

Upvotes: 2

Deepak Kumar Sahu
Deepak Kumar Sahu

Reputation: 453

[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

Upvotes: 9

Tushar Koul
Tushar Koul

Reputation: 2980

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.

Upvotes: 3

Kupendiran iOS
Kupendiran iOS

Reputation: 217

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,

Upvotes: 1

Eric
Eric

Reputation: 16931

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

Upvotes: 2

R&#233;my Virin
R&#233;my Virin

Reputation: 3379

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

}

Upvotes: 5

Lucas van Dongen
Lucas van Dongen

Reputation: 9878

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.

Upvotes: 2

Denis Kozhukhov
Denis Kozhukhov

Reputation: 1215

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.

Upvotes: 9

Danilo
Danilo

Reputation: 3327

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

Upvotes: 8

buildsucceeded
buildsucceeded

Reputation: 4243

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;

Upvotes: 3

Christian
Christian

Reputation: 1714

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

Upvotes: -2

Related Questions