How to stretch a UIImageView without stretching the center?
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.
(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.
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.
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;
}