If I put only an image in a button and set the imageEdgeInsets more close to the top, the image stays centered and all works as expected:
[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, 0.0)];
If I put only a text in a button and set titleEdgeInsets more close to the bottom, the text stays centered and all works as expected:
[button setTitle:title forState:UIControlStateNormal];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, 0.0, -30, 0.0)];
But, if i put the 4 lines together the text interfere with the image and both lost the center alignment.
All my images has 30 pixels width, and if i put 30 in the left parameter of UIEdgeInsetMake for setTitleEdgeInsets, the text is centered again. The problem is that the image never gets centered because appears that it is dependent of the button.titleLabel size. I already tried many calculations with button size, image size, titleLabel size and never get both perfectly centered.
Someone already have the same problem?
Thanks in advance.
Source: Tips4all
Found how.
ReplyDeleteFirst, configure the text of titleLabel (because of styles, i.e, bold, italic, etc). Then, use setTitleEdgeInsets considering the width of your image:
[button setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateNormal];
[button.titleLabel setFont:[UIFont boldSystemFontOfSize:10.0]];
[button setTitleEdgeInsets:UIEdgeInsetsMake(0.0, -image.size.width, -25.0, 0.0)]; // Left inset is the negative of image width.
After that, use setTitleEdgeInsets considering the width of text bounds:
[button setImage:image forState:UIControlStateNormal];
[button setImageEdgeInsets:UIEdgeInsetsMake(-15.0, 0.0, 0.0, -button.titleLabel.bounds.size.width)]; // Right inset is the negative of text bounds width.
Now the image and the text will be centered (in this example, the image appears above the text).
Cheers.
For what it's worth, here's a general solution to positioning the image centered above the text without using any magic numbers:
ReplyDelete// the space between the image and text
CGFloat spacing = 6.0;
// get the size of the elements here for readability
CGSize imageSize = button.imageView.frame.size;
CGSize titleSize = button.titleLabel.frame.size;
// lower the text and push it left to center it
button.titleEdgeInsets = UIEdgeInsetsMake(
0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
// the text width might have changed (in case it was shortened before due to
// lack of space and isn't anymore now), so we get the frame size again
titleSize = button.titleLabel.frame.size;
// raise the image and push it right to center it
button.imageEdgeInsets = UIEdgeInsetsMake(
- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
Using button.titleLabel.frame.size.width works fine as long the label is short enough not to be truncated. When the label text gets truncated positioning doesn't work. Tanking
ReplyDeleteCGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[botton titleLabel] font]];
works for me even when the label text is truncated.
I looked at existing answers but I also found that setting the button frame is an important first step.
ReplyDeleteHere is a function that I use that takes care of this:
const CGFloat kImageTopOffset = -15;
const CGFloat kTextBottomOffset = -25;
+ (void) centerButtonImageTopAndTextBottom: (UIButton*) button
frame: (CGRect) buttonFrame
text: (NSString*) textString
textColor: (UIColor*) textColor
font: (UIFont*) textFont
image: (UIImage*) image
forState: (UIControlState) buttonState
{
button.frame = buttonFrame;
[button setTitleColor: (UIColor*) textColor
forState: (UIControlState) buttonState];
[button setTitle: (NSString*) textString
forState: (UIControlState) buttonState ];
[button.titleLabel setFont: (UIFont*) textFont ];
[button setTitleEdgeInsets: UIEdgeInsetsMake( 0.0, -image.size.width, kTextBottomOffset, 0.0)];
[button setImage: (UIImage*) image
forState: (UIControlState) buttonState ];
[button setImageEdgeInsets: UIEdgeInsetsMake( kImageTopOffset, 0.0, 0.0,- button.titleLabel.bounds.size.width)];
}
There are some great examples in here, but I couldn't get this to work in all cases when also dealing with multiple lines of text (text wrapping). To finally get it to work I combined a couple of the techniques:
ReplyDeleteI used Jesse Crossen example above. However, I fixed a text height
issue and I added the ability to specify a horizontal text margin.
The margin is useful when allowing text to wrap so it doesn't hit
the edge of the button:
// the space between the image and text
CGFloat spacing = 10.0;
float textMargin = 6;
// get the size of the elements here for readability
CGSize imageSize = picImage.size;
CGSize titleSize = button.titleLabel.frame.size;
CGFloat totalHeight = (imageSize.height + titleSize.height + spacing); // get the height they will take up as a unit
// lower the text and push it left to center it
button.titleEdgeInsets = UIEdgeInsetsMake( 0.0, -imageSize.width +textMargin, - (totalHeight - titleSize.height), +textMargin ); // top, left, bottom, right
// the text width might have changed (in case it was shortened before due to
// lack of space and isn't anymore now), so we get the frame size again
titleSize = button.titleLabel.bounds.size;
button.imageEdgeInsets = UIEdgeInsetsMake(-(titleSize.height + spacing), 0.0, 0.0, -titleSize.width ); // top, left, bottom, right
Make sure you setup the text label to wrap
button.titleLabel.numberOfLines = 2;
button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
button.titleLabel.textAlignment = UITextAlignmentCenter;
This will mostly work now. However, I had some buttons that wouldn't render their image correctly. The image was either shifted to far to the right or left (it wasn't centered). So I used an UIButton layout override technique to force the imageView to be centered.
@interface CategoryButton : UIButton
@end
@implementation CategoryButton
- (void)layoutSubviews
{
// Allow default layout, then center imageView
[super layoutSubviews];
UIImageView *imageView = [self imageView];
CGRect imageFrame = imageView.frame;
imageFrame.origin.x = (int)((self.frame.size.width - imageFrame.size.width)/ 2);
imageView.frame = imageFrame;
}
@end
I made a method for @TodCunningham's answer
ReplyDelete-(void) AlignTextAndImageOfButton:(UIButton *)button
{
CGFloat spacing = 2; // the amount of spacing to appear between image and title
button.imageView.backgroundColor=[UIColor clearColor];
button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
button.titleLabel.textAlignment = UITextAlignmentCenter;
// get the size of the elements here for readability
CGSize imageSize = button.imageView.frame.size;
CGSize titleSize = button.titleLabel.frame.size;
// lower the text and push it left to center it
button.titleEdgeInsets = UIEdgeInsetsMake(0.0, - imageSize.width, - (imageSize.height + spacing), 0.0);
// the text width might have changed (in case it was shortened before due to
// lack of space and isn't anymore now), so we get the frame size again
titleSize = button.titleLabel.frame.size;
// raise the image and push it right to center it
button.imageEdgeInsets = UIEdgeInsetsMake(- (titleSize.height + spacing), 0.0, 0.0, - titleSize.width);
}