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

[韓日関係] 首相含む大幅な内閣改造の可能性…早ければ来月10日ごろ=韓国

div not scrolling properly with slimScroll plugin

I am using the slimScroll plugin for jQuery by Piotr Rochala Which is a great plugin for nice scrollbars on most browsers but I am stuck because I am using it for a chat box and whenever the user appends new text to the boxit does scroll using the .scrollTop() method however the plugin's scrollbar doesnt scroll with it and when the user wants to look though the chat history it will start scrolling from near the top. I have made a quick demo of my situation http://jsfiddle.net/DY9CT/2/ Does anyone know how to solve this problem?

Why does this javascript based printing cause Safari to refresh the page?

The page I am working on has a javascript function executed to print parts of the page. For some reason, printing in Safari, causes the window to somehow update. I say somehow, because it does not really refresh as in reload the page, but rather it starts the "rendering" of the page from start, i.e. scroll to top, flash animations start from 0, and so forth. The effect is reproduced by this fiddle: http://jsfiddle.net/fYmnB/ Clicking the print button and finishing or cancelling a print in Safari causes the screen to "go white" for a sec, which in my real website manifests itself as something "like" a reload. While running print button with, let's say, Firefox, just opens and closes the print dialogue without affecting the fiddle page in any way. Is there something with my way of calling the browsers print method that causes this, or how can it be explained - and preferably, avoided? P.S.: On my real site the same occurs with Chrome. In the ex