Natasha The Robot

Currently learning... Swift!                                                                     You can follow me on Twitter here.                                            Subscribe via RSS here.

iOS: How To Make A Stretchable Button With UIEdgeInsetsMake

Posted on

In a given iOS app, there are likely going to be many buttons that have the same background but might be different sizes depending on the screen they’re on (although the new iOS7 button has no background). So for example, a button that says “Save” will be shorter than the button that says “Submit”, but they both might have a purple background.

In this scenario, you wouldn’t want to have a custom background image of each size for each of your buttons. You’re in luck, because the iOS UIEdgeInsetsMake method makes it very easy to take an image like this:

purple_button@2x

and turn it into this:

Screen Shot 2013-10-24 at 6.32.55 AM

The idea is pretty simple. The edges of the original square button with rounded corners need to stay the same, but the parts in the middle could be duplicated over and over again, both vertically and horizontally, to make the middle part of the button as big as it needs to be.

To do that, we can use the UIEdgeInsets, where you can specify how many pixels to the top, left, bottom, and right to IGNORE where stretching the image. The UIEdgeInsets is a struct, with floats for top, left, bottom, and right specified:

typedef struct {
   CGFloat top, left, bottom, right;
} UIEdgeInsets;

Replicating Everything

So I got a project started, where I added a custom button to the storyboard, then created an outlet for that button in my ViewController. In my ViewDidLoad: method, I call a private configureButton method, which initially starts with all the UIEdgeInsets set to setting the UIEdgeInsets to UIEdgeInsetsMake(0, 0, 0, 0):

- (void)configureButton
{
    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(0, 0, 0, 0);
    UIImage *backgroundButtonImage = [[UIImage imageNamed:@"purple_button.png"]
                                      resizableImageWithCapInsets:edgeInsets];
    [self.purpleButton setBackgroundImage:backgroundButtonImage
                                 forState:UIControlStateNormal];
}

With the full image replicated over and over again (since no pixels are ignored in UIEdgeInsets), the resulting button looks like this:
Screen Shot 2013-10-24 at 6.47.11 AM

Scaling Vertically

To scale the image vertically, we need to ignore the right and the left edges as follows:

purple_button_vertically

Everything, between these edges could be replicated over and over again. To do that, we have to ignore the first 8 pixels on the right and on the left, setting the UIEdgeInsets to UIEdgeInsetsMake(0, 8, 0, 8):

- (void)configureButton
{
    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(0, 8, 0, 8);
    UIImage *backgroundButtonImage = [[UIImage imageNamed:@"purple_button.png"]
                                      resizableImageWithCapInsets:edgeInsets];
    [self.purpleButton setBackgroundImage:backgroundButtonImage
                                 forState:UIControlStateNormal];
}

The result is looking much better!

Screen Shot 2013-10-24 at 6.53.42 AM

Scaling Horizontally

Similarly, if you wanted to scale the image just horizontally, you’d need to ignore the first 8 pixels on the top and bottom parts of the original image, and replicate everything in the middle:

purple_button_horizontal-1

So for just scaling horizontally, you’re configureButton method would look like this, with UIEdgeInsets set to UIEdgeInsetsMake(8, 0, 8, 0):

- (void)configureButton
{
    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(8, 0, 8, 0);
    UIImage *backgroundButtonImage = [[UIImage imageNamed:@"purple_button.png"]
                                      resizableImageWithCapInsets:edgeInsets];
    [self.purpleButton setBackgroundImage:backgroundButtonImage
                                 forState:UIControlStateNormal];
}

Now, just like above, the horizontal scaling is fine, but the vertical is messed up:

Screen Shot 2013-10-24 at 7.03.20 AM

Scaling Both Ways

So to scale the original square both horizontally and vertically, we need the image to ignore the first 8 top and bottom pixels, as well as the first 8 right and left pixels:

purple_button_both-1

To do this, we simply set our UIEdgeInsets to ignore 8 pixels all around to UIEdgeInsetsMake(8, 8, 8, 8):

- (void)configureButton
{
    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(8, 8, 8, 8);
    UIImage *backgroundButtonImage = [[UIImage imageNamed:@"purple_button.png"]
                                      resizableImageWithCapInsets:edgeInsets];
    [self.purpleButton setBackgroundImage:backgroundButtonImage
                                 forState:UIControlStateNormal];
}

Voilà! We not have a nice looking button:

purple button

Now, whenever you need a purple button, you can just use that little square image and keep resizing it to any size by just replicating the middle part over and over again while ignoring the odd edges. Enjoy!

  • http://www.paulsolt.com/ PaulSolt

    You should show an example of this using the new Asset Catalog. You can slide and dice in Xcode.

    • http://reorg.co Natasha Murashev

      Sounds cool. Didn’t know that! Do you have any links to resources for more info?

  • Kunal Das

    great explanation