60

I'm making an app where I use UIEdgeInsetsMake for resizableImageWithCapInsets, but I don't understand how does it works exactly, UIEdgeInsetsMake has 4 arguments:

  • Top
  • Left
  • Bottom
  • Right

But they're floats so I don't know how to set that to an image, thanks! :D

Alexander Mistakidis
  • 3,130
  • 2
  • 20
  • 23
pmerino
  • 5,900
  • 11
  • 57
  • 76
  • As AliSoftware said, you're asking about a method that isn't public yet and shouldn't discussed on a public site like this one. For answers regarding prerelease software, it's best to visit the official Apple development forums. – Tom Irving Oct 02 '11 at 17:58
  • 22
    correct order, UIEdgeInsetsMake(top,left,bottom,right); – Kyle C Jun 24 '13 at 22:39
  • In the year 2014, these things are still a mystery to me. I only know how to center text: (0, -width, 0, -width). – sudo Mar 16 '14 at 04:06
  • 3
    @TomIrving And somehow this question is extremely relevant now. I guess it is good you had limited power to act on your comments in 2011. ;) – Abandoned Cart Jul 01 '14 at 21:46

3 Answers3

70

According to the documentation:

You use this method to add cap insets to an image or to change the existing cap insets of an image. In both cases, you get back a new image and the original image remains untouched.

During scaling or resizing of the image, areas covered by a cap are not scaled or resized. Instead, the pixel area not covered by the cap in each direction is tiled, left-to-right and top-to-bottom, to resize the image. This technique is often used to create variable-width buttons, which retain the same rounded corners but whose center region grows or shrinks as needed. For best performance, use a tiled area that is a 1x1 pixel area in size.

So you only need to use the amount of pixels you want to make unstretchable in the values of the UIEdgeInsetsMake function.

Say you have an image of 21x50 points (21x50 pixels in standard definition, 42x100 pixels in Retina "@2x" definition) and want this image to be horizontally stretchable, keeping the 10 points on the left and on the right untouched when stretching the image, but only stretch the 1-point-wide band in the middle. Then you will use UIEdgeInsetsMake(0,10,0,10).

Don't bother that they are floats (that's useful for subpixelling resizing for example, but in practice you will probably only use integers (or floats with no decimal parts)

Be careful, this is an iOS5+ only method, not available prior iOS5. If you use pre-iOS5 SDK, use stretchableImageWithLeftCapWidth:topCapHeight: instead.


[EDIT] Some tip I use since some time, as I never remember in which order the fields of the UIEdgeInsets structure are — and in which order we are supposed to pass the arguments to UIEdgeInsetsMake function — I prefer using the "designated inits" syntax like this:

UIEdgeInsets insets = { .left = 50, .right = 50, .top = 10, .bottom = 10 };

Or when an explicit cast is needed:

UIImage* rzImg = [image resizableImageWithCapInsets:(UIEdgeInsets){
   .left = 50, .right = 50,
   .top = 10, .bottom = 10
}];

I find it more readable, especially to be sure we don't mix the different borders/directions!

Community
  • 1
  • 1
AliSoftware
  • 32,623
  • 6
  • 82
  • 77
  • I think according to the documentation stretchableImageWithLeftCapWidth:topCapHeight: is actually depecrated, not new, in iOS5 https://developer.apple.com/library/ios/#documentation/uikit/reference/UIImage_Class/DeprecationAppendix/AppendixADeprecatedAPI.html#//apple_ref/occ/instm/UIImage/stretchableImageWithLeftCapWidth:topCapHeight: – Jarsen Mar 06 '12 at 18:52
  • 1
    Yep that's what I explain in my answer too ;) – AliSoftware Mar 07 '12 at 10:34
  • For @2x images, should the stretchable bit still be 1x1 pixel? For example image.png is 21x50 and image@2x.png is 41x100? – jhabbott Dec 02 '12 at 05:08
  • 5
    Measures are always using points, not pixels (1pt=1px on non-retina screens, and 1pt=2px on retina screens). So you image will always be 21x50 **points** both in retina (42x100px) and non-retina (21x50px). You should always use **points** (and not pixels) everywhere in your code. That's what I already explains in my answer by the way. `UIEdgeInsetsMake` uses points, not pixels, too. And the deprecated `stretchableImageWithLeftCapWidth:topCapHeight:` did use points too. So the stretchable bit was meant to be 1 **point** wide, which matches **2 pixels** for the `@2x` image (for which 1pt=2px) – AliSoftware Dec 03 '12 at 03:05
  • 1
    According to the documentation: "For best performance, use a tiled area that is a 1x1 pixel area in size.". As @jhabbott asked, is it good to go for 1 pixel instead of 1 point in case of retina? (If performance is considered important and you only want to support retina devices). – Klaas Jan 12 '13 at 23:13
  • I believe this is a typo in the documentation. It should say "1x1 point area in size", not "1x1 pixel area in size". _(as 1x1 pixel consist of 0.5 point, i.e half-pixels on non-Retina devices, generating sub-pixels computations that are not the best for performance)_ – AliSoftware Jan 12 '13 at 23:28
  • 1
    I doubt it is a typo. The performance improvement is probably down to the fact that for a 1x1 pixel area you can sample the colour and fill the whole area in that colour. If the area is 2x2 then you need to either stretch or tile the sub-image (according to the `UIImageResizingMode`). I would work on the basis that for best performance make your stretchable areas 1x1 pixels (not points) on retina and non-retina displays. So for example for `UIEdgeInsetsMake(10, 10, 10, 10)` use 21x21 @1x and 41x41 @2x. – jhabbott Jan 13 '13 at 00:48
  • Yeah, could be too, your arguments make also sense… So in fact I don't really know which of those two options are right, both make sense (we would need to ask Apple about it directly) – AliSoftware Jan 13 '13 at 01:56
  • @jhabbott, Ali, so which one is it? :) What do *you* do in your apps? – Tyson Mar 21 '13 at 10:56
  • 2
    @Tyson I go by what the docs say - In cases where the stretchable area is a single colour I make a 1x1 *pixel* stretchable area for best performance. – jhabbott Mar 21 '13 at 12:07
  • OK. I guess I'll trust the docs. But, I have seen typos in the docs before. So, can anyone from Apple confirm? If so, please update the documentation to make it clear that "1x1 pixel" is not a typo. – ma11hew28 Oct 16 '13 at 18:38
  • 1
    +1 for "designated inits". I can never remember the argument order either, and didn't know about this technique. – brainjam May 01 '14 at 20:42
10

But they're floats so I don't know how to set that to an image

This is a UIImage instance method. So, the way you use resizableImageWithCapInsets to create in an image is to start by sending that message to an image (a UIImage).

Cool new feature: note that if the edge insets are all zero, the image will be tiled. This works as a background to anything that accepts a background image. It even works in a UIImageView.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 1
    For tiling, you can also do `view.backgroundColor = [UIColor colorWithPatternImage:image]`, which is fairly self-explanatory. Otherwise if you use your tiling trick, I would add a comment, like `// tiles image`. – ma11hew28 Oct 16 '13 at 18:42
3

I just go through this article which cleared all my concept regarding UIEdgeInsetsMake

iOS: How To Make A Stretchable Button With UIEdgeInsetsMake

Azhar
  • 20,500
  • 38
  • 146
  • 211