Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 527 Vote(s) - 3.52 Average
  • 1
  • 2
  • 3
  • 4
  • 5
UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?

#1
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 interferes 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 it 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 had the same problem?
Reply

#2
I looked at existing answers but I also found that setting the button frame is an important first step.

Here 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)];
}
Reply

#3
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:

1. I 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

2. Make sure you setup the text label to wrap

> button.titleLabel.numberOfLines = 2;
> button.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
> button.titleLabel.textAlignment = UITextAlignmentCenter;

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




Reply

#4
Don't fight the system. If your layouts become too complex to manage using Interface Builder + perhaps some simple configuration code, do the layouts manually in a simpler way using `layoutSubviews` - that's what it's for! Everything else will amount to hacks.

Create a UIButton subclass and override its `layoutSubviews` method to align your text & image programmatically. Or use something like

[To see links please register here]

so you can implement layoutSubviews using a block.
Reply

#5
I made a method for @TodCunningham's answer

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




Reply

#6
Or you can just use this category:

@interface UIButton (VerticalLayout)

- (void)centerVerticallyWithPadding:(float)padding;
- (void)centerVertically;

@end


@implementation UIButton (VerticalLayout)

- (void)centerVerticallyWithPadding:(float)padding
{
CGSize imageSize = self.imageView.frame.size;
CGSize titleSize = self.titleLabel.frame.size;

CGFloat totalHeight = (imageSize.height + titleSize.height + padding);

self.imageEdgeInsets = UIEdgeInsetsMake(- (totalHeight - imageSize.height),
0.0f,
0.0f,
- titleSize.width);

self.titleEdgeInsets = UIEdgeInsetsMake(0.0f,
- imageSize.width,
- (totalHeight - titleSize.height),
0.0f);

}


- (void)centerVertically
{
const CGFloat kDefaultPadding = 6.0f;

[self centerVerticallyWithPadding:kDefaultPadding];
}


@end
Reply

#7
Assuming that you want both text and image to be centered horizontally, image above text:
Center the text from interface builder and add a top inset (making room for the image). (leave the left inset to 0). Use interface builder to choose image - it's actual position will be set from code, so don't worry that things will not look ok in IB. Unlike other answers above, this actually works on all currently supported ios versions (5,6 and 7).

In code, just discard the ImageView of the button (by setting the button's image to null) after grabbing the image (this will also automatically center the text, wrapped if necessary). Then instantiate your own ImageView with the same frame size and image and position it in the middle.

This way you can still choose the image from interface builder (though it will not be aligned in IB as in simulator, but then again, other solutions are not compatible across all supported ios versions)
Reply

#8
You can do it with this Swift extension, which was based in part on Jesse Crossen's answer:

extension UIButton {
func centerLabelVerticallyWithPadding(spacing:CGFloat) {
// update positioning of image and title
let imageSize = self.imageView.frame.size
self.titleEdgeInsets = UIEdgeInsets(top:0,
left:-imageSize.width,
bottom:-(imageSize.height + spacing),
right:0)
let titleSize = self.titleLabel.frame.size
self.imageEdgeInsets = UIEdgeInsets(top:-(titleSize.height + spacing),
left:0,
bottom: 0,
right:-titleSize.width)

// reset contentInset, so intrinsicContentSize() is still accurate
let trueContentSize = CGRectUnion(self.titleLabel.frame, self.imageView.frame).size
let oldContentSize = self.intrinsicContentSize()
let heightDelta = trueContentSize.height - oldContentSize.height
let widthDelta = trueContentSize.width - oldContentSize.width
self.contentEdgeInsets = UIEdgeInsets(top:heightDelta/2.0,
left:widthDelta/2.0,
bottom:heightDelta/2.0,
right:widthDelta/2.0)
}
}

This defines a function `centerLabelVerticallyWithPadding` that sets the title and image insets appropriately.

It also sets the contentEdgeInsets, which I believe is necessary to ensure that `intrinsicContentSize` still works correctly, which would need to use Auto Layout.

I believe all solutions which subclass UIButton are technically illegitimate, since you are not supposed to subclass UIKit controls. I.e., in theory they might break in future releases.
Reply

#9
My use case made insets unmanageable:

1. background image on button stays consistent
2. dynamic text and image changes where the string length and image size varies

This is what I ended up doing and I'm pretty happy with it:

- Create the button on the storyboard with a background image (round circle with blur and color).

- Declare a UIImageView in my class:

@implementation BlahViewController {
UIImageView *_imageView;
}

- Create image view instance on init:

-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
_imageView = [[UIImageView alloc] initWithCoder:aDecoder];
}
return self;
}
- In viewDidLoad add a new layer to the button for our image view and set text alignment:

[self.btn addSubview:_imageView];
[self.btn.titleLabel setTextAlignment:NSTextAlignmentCenter];
- In the button click method add my chosen overlay image to the image view, size it to fit the image and center it in the button but move it up 15 so I can put the text offset below it:

[_imageView setImage:[UIImage imageNamed:@"blahImageBlah]];
[_imageView sizeToFit];
_imageView.center = CGPointMake(ceilf(self.btn.bounds.size.width / 2.0f),
ceilf((self.btn.bounds.size.height / 2.0f) - 15));
[self.btn setTitle:@"Some new text" forState:UIControlStateNormal];

Note: ceilf() is important to ensure it's on a pixel boundary for image quality.
Reply

#10
Subclass UIButton

- (void)layoutSubviews {
[super layoutSubviews];
CGFloat spacing = 6.0;
CGSize imageSize = self.imageView.image.size;
CGSize titleSize = [self.titleLabel sizeThatFits:CGSizeMake(self.frame.size.width, self.frame.size.height - (imageSize.height + spacing))];
self.imageView.frame = CGRectMake((self.frame.size.width - imageSize.width)/2, (self.frame.size.height - (imageSize.height+spacing+titleSize.height))/2, imageSize.width, imageSize.height);
self.titleLabel.frame = CGRectMake((self.frame.size.width - titleSize.width)/2, CGRectGetMaxY(self.imageView.frame)+spacing, titleSize.width, titleSize.height);
}

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through