Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stretch a UIImageView without stretching the center?

Tags:

ios

iphone

enter image description here

I want to stretch the left and right sides of the picture shown above, and keep the middle arrow unstretched. How can I do that?

like image 278
njuxjy Avatar asked Mar 15 '13 07:03

njuxjy


4 Answers

If you don't like to bother Photoshop and if the image has not to by dynamically resized, you can use my category method on UIImage.

Your sample image named "17maA.png" is 124 px wide. So you can easily create a 300 px wide version with this:

UIImage *image = [UIImage imageNamed:@"17maA"];
UIImage *newImage = [image pbResizedImageWithWidth:300 andTiledAreaFrom:10 to:20 andFrom:124-20 to:124-10];

This is the category method:

- (UIImage *)pbResizedImageWithWidth:(CGFloat)newWidth andTiledAreaFrom:(CGFloat)from1 to:(CGFloat)to1 andFrom:(CGFloat)from2 to:(CGFloat)to2  {
    NSAssert(self.size.width < newWidth, @"Cannot scale NewWidth %f > self.size.width %f", newWidth, self.size.width);

    CGFloat originalWidth = self.size.width;
    CGFloat tiledAreaWidth = (newWidth - originalWidth)/2;

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(originalWidth + tiledAreaWidth, self.size.height), NO, self.scale);

    UIImage *firstResizable = [self resizableImageWithCapInsets:UIEdgeInsetsMake(0, from1, 0, originalWidth - to1) resizingMode:UIImageResizingModeTile];
    [firstResizable drawInRect:CGRectMake(0, 0, originalWidth + tiledAreaWidth, self.size.height)];

    UIImage *leftPart = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    UIGraphicsBeginImageContextWithOptions(CGSizeMake(newWidth, self.size.height), NO, self.scale);

    UIImage *secondResizable = [leftPart resizableImageWithCapInsets:UIEdgeInsetsMake(0, from2 + tiledAreaWidth, 0, originalWidth - to2) resizingMode:UIImageResizingModeTile];
    [secondResizable drawInRect:CGRectMake(0, 0, newWidth, self.size.height)];

    UIImage *fullImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();

    return fullImage;
}
like image 185
Klaas Avatar answered Nov 01 '22 02:11

Klaas


Use Photoshop or another image editing tool to split the image into two pieces: edge and center. The edge image can be one pixel wide and used for left and right edges; the center is however many pixels wide that capture the arrow. You can then put three UIImageView instances next to each other: left edge, center, and right edge. The left and right edges are resized to the desired width, while the center keeps its original dimensions.

like image 38
Alex Reynolds Avatar answered Nov 01 '22 02:11

Alex Reynolds


Klaas's answer really helped me, same in Swift 3.0:

static func stretchImageSides(image: UIImage, newWidth: CGFloat) -> UIImage? {
    guard image.size.width < newWidth else {
        return image
    }

    let originalWidth = image.size.width
    let tiledAreaWidth = ceil(newWidth - originalWidth) / 2.0;

    UIGraphicsBeginImageContextWithOptions(CGSize(width: originalWidth + tiledAreaWidth, height: image.size.height),
                                           false, image.scale)

    let firstResizable = image.resizableImage(withCapInsets: UIEdgeInsets(top: 0,
                                                                          left: 0,
                                                                          bottom: 0,
                                                                          right: originalWidth - 3),
                                              resizingMode: .stretch)
    firstResizable.draw(in: CGRect(x: 0, y: 0, width: originalWidth + tiledAreaWidth, height: image.size.height))

    let leftPart = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: image.size.height),
                                           false, image.scale)

    let secondResizable = leftPart?.resizableImage(withCapInsets: UIEdgeInsets(top: 0,
                                                                              left: tiledAreaWidth + originalWidth - 3,
                                                                              bottom: 0,
                                                                              right: 0),
                                              resizingMode: .stretch)
    secondResizable?.draw(in: CGRect(x: 0, y: 0, width: newWidth, height: image.size.height))

    let fullImage = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()

    return fullImage
}
  • You just need to provide an image, with desired new Width, it will do the horizontal stretching for you.
like image 3
Arashk Avatar answered Nov 01 '22 01:11

Arashk


(Not much help, but there is support for the opposite: stretching an image's centre and keeping the edge parts the same size: see Stretching an UIImage while preserving the corners.)

To cook up your own solution, you can could create a component that contains three parts: a left image, central image, right image. Then you handle size changes to your component by ensuring the central part remains the same width, while the left/right components (images) stretch.

Alternative strategy: create a component that is two images stacked on top of each other. The lower image is the flat bar bit (without triangle) and stretches horizontally to fill the available space. The upper image is the triangle central part, and is centred in the available space. This would give the effect you're looking for.

Also, there's an app (not free!) that helps you create nice components with stretchable qualities: PaintCode.

like image 1
occulus Avatar answered Nov 01 '22 01:11

occulus