Skip to main content

UIButton: how to center an image and a text using imageEdgeInsets and titleEdgeInsets?


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: Tips4allCCNA FINAL EXAM

Comments

  1. Found how.

    First, 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.

    ReplyDelete
  2. For what it's worth, here's a general solution to positioning the image centered above the text without using any magic numbers:

    // 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);

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

    CGSize titleSize = [[[button titleLabel] text] sizeWithFont:[[botton titleLabel] font]];


    works for me even when the label text is truncated.

    ReplyDelete
  4. 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)];
    }

    ReplyDelete
  5. 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:


    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

    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

    ReplyDelete
  6. 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);
    }

    ReplyDelete

Post a Comment

Popular posts from this blog

Why is this Javascript much *slower* than its jQuery equivalent?

I have a HTML list of about 500 items and a "filter" box above it. I started by using jQuery to filter the list when I typed a letter (timing code added later): $('#filter').keyup( function() { var jqStart = (new Date).getTime(); var search = $(this).val().toLowerCase(); var $list = $('ul.ablist > li'); $list.each( function() { if ( $(this).text().toLowerCase().indexOf(search) === -1 ) $(this).hide(); else $(this).show(); } ); console.log('Time: ' + ((new Date).getTime() - jqStart)); } ); However, there was a couple of seconds delay after typing each letter (particularly the first letter). So I thought it may be slightly quicker if I used plain Javascript (I read recently that jQuery's each function is particularly slow). Here's my JS equivalent: document.getElementById('filter').addEventListener( 'keyup', function () { var jsStart = (new Date).getTime()...

Is it possible to have IF statement in an Echo statement in PHP

Thanks in advance. I did look at the other questions/answers that were similar and didn't find exactly what I was looking for. I'm trying to do this, am I on the right path? echo " <div id='tabs-".$match."'> <textarea id='".$match."' name='".$match."'>". if ($COLUMN_NAME === $match) { echo $FIELD_WITH_COLUMN_NAME; } else { } ."</textarea> <script type='text/javascript'> CKEDITOR.replace( '".$match."' ); </script> </div>"; I am getting the following error message in the browser: Parse error: syntax error, unexpected T_IF Please let me know if this is the right way to go about nesting an IF statement inside an echo. Thank you.