I have a UIButton
with text \"Explore the app\" and UIImage
(>)
In Interface Builder
it looks like:
[ (>) Explore t
In Swift:
override func layoutSubviews(){
super.layoutSubviews()
let inset: CGFloat = 5
if var imageFrame = self.imageView?.frame,
var labelFrame = self.titleLabel?.frame {
let cumulativeWidth = imageFrame.width + labelFrame.width + inset
let excessiveWidth = self.bounds.width - cumulativeWidth
labelFrame.origin.x = excessiveWidth / 2
imageFrame.origin.x = labelFrame.origin.x + labelFrame.width + inset
self.imageView?.frame = imageFrame
self.titleLabel?.frame = labelFrame
}
}
Set the imageEdgeInset
and titleEdgeInset
to move the components around within your image. You could also create a button using those graphics that is full size, and use that as the background image for the button (then use titleEdgeInsets
to move the title around).
What about subclassing UIButton
and overriding layoutSubviews
?
Then post-processing the locations of self.imageView
& self.titleLabel
Building off the answer by @split...
The answer is fantastic, but it ignores the fact that the button may have custom image and title edge insets that are set beforehand (e.g. in storyboard).
For instance, you may want the image have some padding from the top and bottom of the container, but still move the image to the right side of the button.
I extended the concept with this method:-
- (void) moveImageToRightSide {
[self sizeToFit];
CGFloat titleWidth = self.titleLabel.frame.size.width;
CGFloat imageWidth = self.imageView.frame.size.width;
CGFloat gapWidth = self.frame.size.width - titleWidth - imageWidth;
self.titleEdgeInsets = UIEdgeInsetsMake(self.titleEdgeInsets.top,
-imageWidth + self.titleEdgeInsets.left,
self.titleEdgeInsets.bottom,
imageWidth - self.titleEdgeInsets.right);
self.imageEdgeInsets = UIEdgeInsetsMake(self.imageEdgeInsets.top,
titleWidth + self.imageEdgeInsets.left + gapWidth,
self.imageEdgeInsets.bottom,
-titleWidth + self.imageEdgeInsets.right - gapWidth);
}
Another simple way (that is NOT iOS 9 only) is to subclass UIButton to override these two methods
override func titleRectForContentRect(contentRect: CGRect) -> CGRect {
var rect = super.titleRectForContentRect(contentRect)
rect.origin.x = 0
return rect
}
override func imageRectForContentRect(contentRect: CGRect) -> CGRect {
var rect = super.imageRectForContentRect(contentRect)
rect.origin.x = CGRectGetMaxX(contentRect) - CGRectGetWidth(rect)
return rect
}
contentEdgeInsets
is already taken into account by using super.
This solution works iOS 7 and above
Just subclass UIButton
@interface UIButton (Image)
- (void)swapTextWithImage;
@end
@implementation UIButton (Image)
- (void)swapTextWithImage {
const CGFloat kDefaultPadding = 6.0f;
CGSize buttonSize = [self.titleLabel.text sizeWithAttributes:@{
NSFontAttributeName:self.titleLabel.font
}];
self.titleEdgeInsets = UIEdgeInsetsMake(0, -self.imageView.frame.size.width, 0, self.imageView.frame.size.width);
self.imageEdgeInsets = UIEdgeInsetsMake(0, buttonSize.width + kDefaultPadding, 0, -buttonSize.width);
}
@end
Usage (Somewhere in your class):
[self.myButton setTitle:@"Any text" forState:UIControlStateNormal];
[self.myButton swapTextWithImage];