Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using stretchable images in Xcode storyboards

I'm using storyboards to layout my view controllers and I would like to use stretchable images for my buttons (so that I don't need to generate several images with different sizes).

Is this possible to do directly in storyboards without writing any code? I'm really liking the possibility to use storyboards for all graphic stuff and keep the code clean from UI stuff, but it seems like I can't get away with it here.

If it is not possible, what would you suggest otherwise?

like image 235
Joel Avatar asked Aug 01 '12 18:08

Joel


People also ask

How do I resize an image in Xcode storyboard?

If the image looked too big, Interface Builder will complain that preview image doesn't match constraints, you will see a little yellow arrow. Click on it, choose the warning and click again – Xcode will offer you to "Update frame". That will resize the image correctly if necessary.


1 Answers

Update for iOS 7+
iOS 7+ now supports stretchable images natively via the asset catalog. Using the asset catalog you can now specify how images are sliced and how they scale (stretch or tile). These asset catalog attributes for the image will be reflected immediately in storyboard. Great new improvement. For more info, see Apple's docs on the Asset Catalog

For deploying to iOS versions before 7:
It's a little known fact, but you can absolutely set cap insets of an image using only Interface Builder/Storyboard and the stretching properties in the attributes inspector. Thanks to Victor for the original answer.

Looking at the stretching properties in the attributes inspector of a UIImage, the X and Y values are the positions for the stretch starting point, relative to the entire width and height of the image. A value of 0.5 would mean a point in the middle of the image.

The width and height are sizes for the stretchable area relative to the image size. So, setting the width to a value of 1 / imageWidth would set the stretchable area to be 1px wide.

Most stretchable images will stretch from the middle, so using these values for X,Y, Width, & Height will usually work:

X = 0.5 Y = 0.5 Width = 1/imageWidth Height = 1/imageHeight 

Note: Unless you have a very small image you are stretching, this means that width and height properties will be very small (e.g. 0.008) and 0.0 can be used instead. So, practically speaking, 0.5, 0.5, 0.0, 0.0 will almost always work for X,Y, Width & Height.

In the small number of cases that 0.0 does not work for Width and Height this does mean you need to use a calculator to set these values in IB. However, I think that is generally preferable than having to set it programmatically as you will be able to see the resulting stretched image in IB (WYSIWYG).

Stretchable image attributes

Update: Some people have pointed out that although stretching images works in Storyboard using the above suggestions, stretching images on buttons is still broken, even as of iOS7. Not to worry, this is easily addressed by creating a UIButton category that takes care of setting the cap insets for control states:

@implementation UIButton (Stretchable)  /* Automatically set cap insets for the background image. This assumes that    the image is a standard slice size with a 1 px stretchable interior */ - (void)setBackgroundImageStretchableForState:(UIControlState)controlState {     UIImage *image = [self backgroundImageForState:controlState];     if (image)     {        CGFloat capWidth =  floorf(image.size.width / 2);        CGFloat capHeight =  floorf(image.size.height / 2);        UIImage *capImage = [image resizableImageWithCapInsets:                      UIEdgeInsetsMake(capHeight, capWidth, capHeight, capWidth)];         [self setBackgroundImage:capImage forState:controlState];     } } 

Using this category, you can set your stretchable image for your button via Storyboard and then easily ensure that it stretches properly by calling -setBackgroundImageStretchableForState: in your -viewDidLoad. Iterating through your view hierarchy makes it trivial to do this even for a large number of buttons in your view:

NSPredicate *predicate =      [NSPredicate predicateWithFormat:@"self isKindOfClass:%@",[UIButton class]]; NSArray *buttons = [self.view.subviews filteredArrayUsingPredicate:predicate]; for (UIButton *button in buttons)    [button setBackgroundImageStretchableForState:UIControlStateNormal]; 

While this isn't quite as good as having a UIButton subclass which does this automatically for you (subclassing UIButton isn't practical since it's a class cluster), it does give you nearly the same functionality with just a bit of boilerplate code in your viewDidLoad -- you can set all your button images in Storyboard and still get them to properly stretch.

like image 194
memmons Avatar answered Sep 24 '22 20:09

memmons