I have a UIButton
with text \"Explore the app\" and UIImage
(>)
In Interface Builder
it looks like:
[ (>) Explore t
Here is my own way to do the thing, (after about 10 years)
class CustomButton: Button {
var didLayout: Bool = false // The code must be called only once
override func layoutSubviews() {
super.layoutSubviews()
if !didLayout, let imageView = imageView, let titleLabel = titleLabel {
didLayout = true
let stack = UIStackView(arrangedSubviews: [titleLabel, imageView])
addSubview(stack)
stack.edgesToSuperview() // I use TinyConstraints library. You could handle the constraints directly
stack.axis = .horizontal
}
}
}
My solution to this is quite 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);
// 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);
This creates a right-aligned button, like so:
[ button label (>)]
The button doesn't adjust it's width according to the context, so space will appear on the left of the label. You could solve this by calculating the button's frame width from the buttonLabelSize.width and the buttonImageSize.width.
Building on previous answers. If you want to have a margin between the icon and the title of the button, the code has to change a little to prevent floating of the label and icon above the bounds of intrinsically sized buttons.
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)
The last code line is important for the intrinsically content size calculation for auto layout.
Raymond W's answer is best here. Subclass UIButton with custom layoutSubviews. Extremely simple to do, here's a layoutSubviews implementation that worked for me:
- (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;
}
Forcing 'right-to-left' for the button is not an option if your app supports both 'left-to-right' and 'right-to-left'.
The solution that worked for me is a subclass that can be added to the button in the Storyboard and works well with constraints (tested in 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)
}
}
}
Where 'padding' would be the space between the title and the image.