UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?
For what it's worth, here's a general solution to positioning the image centered above the text without using any magic numbers. Note that the following code is outdated and you should probably use one of the updated versions below:
// the space between the image and text
CGFloat spacing = 6.0;
// lower the text and push it left so it appears centered
// below the image
CGSize imageSize = button.imageView.frame.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
// raise the image and push it right so it appears centered
// above the text
CGSize titleSize = button.titleLabel.frame.size;
button.imageEdgeInsets = UIEdgeInsetsMake(
- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
The following version contains changes to support iOS 7+ that have been recommended in comments below. I haven't tested this code myself, so I'm not sure how well it works or whether it would break if used under previous versions of iOS.
// the space between the image and text
CGFloat spacing = 6.0;
// lower the text and push it left so it appears centered
// below the image
CGSize imageSize = button.imageView.image.size;
button.titleEdgeInsets = UIEdgeInsetsMake(
0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
// raise the image and push it right so it appears centered
// above the text
CGSize titleSize = [button.titleLabel.text sizeWithAttributes:@{NSFontAttributeName: button.titleLabel.font}];
button.imageEdgeInsets = UIEdgeInsetsMake(
- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
// increase the content height to avoid clipping
CGFloat edgeOffset = fabsf(titleSize.height - imageSize.height) / 2.0;
button.contentEdgeInsets = UIEdgeInsetsMake(edgeOffset, 0.0, edgeOffset, 0.0);
Swift 5.0 version
extension UIButton {
func alignVertical(spacing: CGFloat = 6.0) {
guard let imageSize = imageView?.image?.size,
let text = titleLabel?.text,
let font = titleLabel?.font
else { return }
titleEdgeInsets = UIEdgeInsets(
top: 0.0,
left: -imageSize.width,
bottom: -(imageSize.height + spacing),
right: 0.0
)
let titleSize = text.size(withAttributes: [.font: font])
imageEdgeInsets = UIEdgeInsets(
top: -(titleSize.height + spacing),
left: 0.0,
bottom: 0.0,
right: -titleSize.width
)
let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0
contentEdgeInsets = UIEdgeInsets(
top: edgeOffset,
left: 0.0,
bottom: edgeOffset,
right: 0.0
)
}
}
Aligning text and image on UIButton with imageEdgeInsets and titleEdgeInsets
I agree the documentation on imageEdgeInsets
and titleEdgeInsets
should be better, but I figured out how to get the correct positioning without resorting to trial and error.
The general idea is here at this question, but that was if you wanted both text and image centered. We don't want the image and text to be centered individually, we want the image and the text to be centered together as a single entity. This is in fact what UIButton already does so we simply need to adjust the spacing.
CGFloat spacing = 10; // the amount of spacing to appear between image and title
tabBtn.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
tabBtn.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);
I also turned this into a category for UIButton so it will be easy to use:
UIButton+Position.h
@interface UIButton(ImageTitleCentering)
-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing;
@end
UIButton+Position.m
@implementation UIButton(ImageTitleCentering)
-(void) centerButtonAndImageWithSpacing:(CGFloat)spacing {
self.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, spacing);
self.titleEdgeInsets = UIEdgeInsetsMake(0, spacing, 0, 0);
}
@end
So now all I have to do is:
[button centerButtonAndImageWithSpacing:10];
And I get what I need every time. No more messing with the edge insets manually.
EDIT: Swapping Image and Text
In response to @Javal in comments
Using this same mechanism, we can swap the image and the text. To accomplish the swap, simply use a negative spacing but also include the width of the text and the image. This will require frames to be known and layout performed already.
[self.view layoutIfNeeded];
CGFloat flippedSpacing = -(desiredSpacing + button.currentImage.size.width + button.titleLabel.frame.size.width);
[button centerButtonAndImageWithSpacing:flippedSpacing];
Of course you will probably want to make a nice method for this, potentially adding a second category method, this is left as an exercise to the reader.
UIButton align image left and center text
All the properties you mentioned above can be set in the init
of the UIButton
there is absolutely no need to set them in layoutSubviews
or willMove(toWindow
.layoutSubviews
will be called multiple times so setting these properties again n agin in here makes no sense. willMove(toWindow
will be called when button is added to some view and button is loaded but you dont have to wait till then to set these properties. Because you already have a subclass of button, so I would suggest doing
class SomeButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.contentHorizontalAlignment = .center
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
By the way creating a subclass of UIButton
is not recommended, so if you wanna simply assign these properties to your button you can rather have a extension to UIButton
extension UIButton {
func applyStyle() {
self.titleEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
self.contentHorizontalAlignment = .center
}
}
EDIT:
Is this what you want??
No matter what the text is, text is always in centre and image is to its left with 10 pixel padding
EDIT 2:
As OP has confirmed that, he wants the button to be styled as showed in images above, posting the code to achieve the same
class SomeButton: UIButton {
var titleFont: UIFont! = nil
var textSize: CGFloat = 0
let imageWidth: CGFloat = 20
let buttonHeight: CGFloat = 30
override init(frame: CGRect) {
super.init(frame: frame)
self.titleFont = titleLabel!.font
self.setTitle("here", for: .normal)
self.setTitleColor(UIColor.red, for: .normal)
self.setImage(UIImage(named: "hand"), for: .normal)
self.backgroundColor = UIColor.green
}
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
if let string = self.title(for: .normal) {
textSize = string.widthOfString(usingFont: self.titleFont)
//30 because imageWidth + 10 padding
return CGRect(origin: CGPoint(x: 30, y: 0), size: CGSize(width: textSize + 30, height: buttonHeight))
}
return CGRect.zero
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
return CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: imageWidth, height: buttonHeight))
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override var intrinsicContentSize: CGSize {
//60 because you need eauql padding on both side 30 + 30 = 60
return CGSize(width: textSize + 60, height: buttonHeight)
}
}
extension String {
func widthOfString(usingFont font: UIFont) -> CGFloat {
let fontAttributes = [NSAttributedString.Key.font: font]
let size = self.size(withAttributes: fontAttributes)
return size.width
}
}
Hope it helps
Align image at the right corner of UIButton irrespective of text length
Use override
method of UIButton
and manage your title and image frame.
Here is UIButton
subclass.
class ButtonIconRight: UIButton {
override func imageRect(forContentRect contentRect:CGRect) -> CGRect {
var imageFrame = super.imageRect(forContentRect: contentRect)
imageFrame.origin.x = self.bounds.width - imageFrame.width
return imageFrame
}
override func titleRect(forContentRect contentRect:CGRect) -> CGRect {
var titleFrame = super.titleRect(forContentRect: contentRect)
if (self.currentImage != nil) {
titleFrame.origin.x = 0
}
return titleFrame
}
}
The space between title and text on UIButton is not correct when adjusting titleEdgeInsets and imageEdgeInsets
You Didn't set imageEdgeInsets
like this :
rightButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
So total Code is here :
leftButton.contentHorizontalAlignment = .left
leftButton.imageView?.backgroundColor = .red
leftButton.titleLabel?.backgroundColor = .gray
leftButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
rightButton.contentHorizontalAlignment = .center
rightButton.imageView?.backgroundColor = .red
rightButton.titleLabel?.backgroundColor = .gray
rightButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
rightButton.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 0)
Reason:
Actually you need to set imageEdgeInsets
both time left alignment or center alignment
But when its left alignment image there is no space to set Insets at the right side.
Seet this code :
leftButton.imageEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 100)
Here is also no space .
So home you get some idea how insets work :
For your more Help :
Click here
ios - Left Align Image and Place Text in Center of UIButton
@Rohan The issue is let margin = 30 - image.size.width / 2
. You are using image width to find out the margin.The Images (facebook and google
) size are different(I am assuming). So according to your code, label left margin must change with image width. So if you want to achieve You have to keep both image size similar according to your code.
Custom UIButton imageEdgeInsets/titleEdgeInsets not working
Try changing button style to default via buttons attribute inspector.
Related Topics
Iphone Smooth Sketch Drawing Algorithm
How to Make Uilabel Display Outlined Text
Ios9 Getting Error "An Ssl Error Has Occurred and a Secure Connection to the Server Cannot Be Made"
Modal View Controllers - How to Display and Dismiss
Xcode 6 - Xcassets For Universal Image Support
How to Add Objects to a Uiscrollview That Extend Beyond Uiview from Storyboard
Library Not Loaded: @Rpath/Fblpromises.Framework/Fblpromises iOS 13.3.1
Add Placeholder Text Inside Uitextview in Swift
Programmatically Create a Uiview With Color Gradient
Detecting Color of Iphone/Ipad/Ipod Touch
Run Code Only After Asynchronous Function Finishes Executing
How to Get a Plist as a Dictionary in Swift
How to Set the Width of a Cell in a Uitableview in Grouped Style
How to Use Facebook iOS Sdk on iOS 10