I know this topic has lots of Q&A, I read them all and still couldn't get it going...
I'm trying to stretch an image, while keeping its rounded edges.
This is my image (104x77px):
this is the code I'm using:
UIImage *bg = [UIImage imageNamed:#"btnBg"];
UIEdgeInsets insets = UIEdgeInsetsMake((bg.size.height - 1)/2, (bg.size.width - 1)/2, (bg.size.height - 1)/2, (bg.size.width - 1)/2);
bg = [bg resizableImageWithCapInsets:insets];
UIImageView *imgView = [[UIImageView alloc] initWithFrame:CGRectMake(20, 20, 50, 44)];
imgView.image = bg;
UIImageView *imgView2 = [[UIImageView alloc] initWithFrame:CGRectMake(20, 70, 250, 44)];
imgView2.image = bg;
[self.view addSubview:imgView];
[self.view addSubview:imgView2];
and this is the problem:
As you can see each image has different edges.
What am I doing wrong?!
I think what you are looking for is a stretchable image
[[UIImage imageNamed:#"someimage.png"] stretchableImageWithLeftCapWidth:5 topCapHeight:5];
As maddy points out this is deprecated in iOS 5, for those of us poor souls having to support iOS 4.3 this is what we use.
Whenever any scaling is involved, images of two different dimensions can't look quite the same. However you can break your image into three parts: the left corners, the middle section (just the parallel lines), and the right corners.
Now to create image views of different sizes, you'll keep the left and right parts constant, and scale the middle section, which won't look distorted as long as you keep the heights constant.
Note that I have give the height of fitInRect as 77 because that's the original image height. You can change this to, say, 44 but keep the number constant for all image views.
Finally, widthToPreserve HAS to be a minimum of 34 (which means the overall Rect width has to be 68 or more) because that's the width of the rounded edge in the original image.
Of course, you can simply all this by having a smaller image, if your target image view height is 44.
This code works:
-(void)viewDidAppear:(BOOL)animated
{
UIImage *img = [UIImage imageNamed:#"img"];
UIImageView *view1 = [self getImageViewFromImage:img widthToPreserve:34 fitInRect:CGRectMake(20, 20, 100, 77)];
UIImageView *view2 = [self getImageViewFromImage:img widthToPreserve:34 fitInRect:CGRectMake(20, 120, 250, 77)];
[self.view addSubview:view1];
[self.view addSubview:view2];
}
-(UIImageView*)getImageViewFromImage:(UIImage*)image widthToPreserve:(float)width fitInRect:(CGRect)rect
{
CGImageRef left, middle, right;
CGRect leftRect, middleRect, rightRect;
UIImage *leftImage, *middleImage, *rightImage;
UIImageView *leftView, *middleView, *rightView;
// calculate CGRect values for the original image
leftRect = CGRectMake(0, 0, width, image.size.height);
middleRect = CGRectMake(width, 0, image.size.width - 2*width, image.size.height);
rightRect = CGRectMake(image.size.width - width, 0, width, image.size.height);
left = CGImageCreateWithImageInRect([image CGImage], leftRect);
middle = CGImageCreateWithImageInRect([image CGImage], middleRect);
right = CGImageCreateWithImageInRect([image CGImage], rightRect);
leftImage = [UIImage imageWithCGImage:left];
middleImage = [UIImage imageWithCGImage:middle];
rightImage = [UIImage imageWithCGImage:right];
leftView = [[UIImageView alloc] initWithImage:leftImage];
middleView = [[UIImageView alloc] initWithImage:middleImage];
rightView = [[UIImageView alloc] initWithImage:rightImage];
//make your image subviews, with scaling on the middle view
[leftView setFrame:CGRectMake(0, 0, width, rect.size.height)];
[middleView setFrame:CGRectMake(width, 0, rect.size.width - 2*width, rect.size.height)];
[rightView setFrame:CGRectMake(rect.size.width - width, 0, width, rect.size.height)];
//add your 3 subviews into a single image view
UIImageView *imgView = [[UIImageView alloc] initWithFrame:rect];
[imgView addSubview:leftView];
[imgView addSubview:middleView];
[imgView addSubview:rightView];
CGImageRelease(left);
CGImageRelease(middle);
CGImageRelease(right);
return imgView;
}
Screenshot:
You can specify the residing mode for your image. The choices are UIImageResizingModeStretch and UIImageResizingModeTile.
The idea of having a stretchable image is so that you can have a small image file. You should consider using a smaller image to save space in your app bundle. Also, your left and right edge insets don't have to be in the middle. They should be just beyond the curve, which looks to be about 40 points in your image.
Here's the code to use to set the resizing mode:
bg = [bg resizableImageWithCapInsets:insets resizingMode: UIImageResizingModeStretch];
Check this out: Resizable UIImage where outside stretches and inside is kept the same
Basically the opposite of what you want. Hope it helps!
Related
I'm using -drawViewHierarchyInRect:afterScreenUpdates: to draw a view into a UIImage, but it seems no matter what I do the results is always a very low resolution, because the image seems to need to be the same size as the original view?
The UIView is the size of the screen (in this case, 375 x 667), and the content scale factor is 2.0f.
But when I use -drawViewHierarchyInRect:afterScreenUpdates: to draw the view onto the image, it is resized (horribly) to this resolution, even though the contents is a much higher resolution.
I'm using UIGraphicsBeginImageContextWithOptions with a scale of 0.0 to create the image, so I assumed it should draw nicely, but no go. How do you draw the view hierarchy and not lose the detail?
Edit:
Here's some example code (from the answer below, which is giving the same result). It's giving me terribly down-scaled results when trying to draw the UIView hierarchy which includes a high res image:
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, 0);
[self.view drawViewHierarchyInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
imgView.frame = self.view.bounds;
[self.view addSubview:imgView];
[self.view bringSubviewToFront:imgView];
This code works perfectly. It takes screenshot of current view and puts it to new created UIImageView. Please check that
UIGraphicsBeginImageContextWithOptions(self.view.frame.size, NO, 0);
[self.view drawViewHierarchyInRect:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height) afterScreenUpdates:NO];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImageView *imgView = [[UIImageView alloc] initWithImage:image];
imgView.frame = self.view.bounds;
[self.view addSubview:imgView];
[self.view bringSubviewToFront:imgView];
I would like to add an image to the middle of the UINavigationBar. I could do it easily, but the image that I set is always pixelated no matter what resolution options i use.
I tried these two solutions, but it's still pixeled.
//1.
UIImage *logoImage = [UIImage imageNamed:#"logo.png"];
self.navigationItem.titleView = [[UIImageView alloc] initWithImage:logoImage];
//2.
CGRect myImageRect = CGRectMake(0, 0, 30, 30);
UIImageView *myImage = [[UIImageView alloc] initWithFrame:myImageRect];
[myImage setImage:[UIImage imageNamed:#"logo.png"]];
myImage.contentMode = UIViewContentModeScaleAspectFill;
self.navigationItem.titleView = myImage;
I've tried to play with the image sizes, but the result was always abnormal. Is it any other solution that I could try? Or is there any recommended resolution for the png? I tried 100 x 100 px, 44 x 44 px and 30 x 30 px.
I am trying to figure out to get a single image pit of 2 image views like instagram
These are the 2 images. Thanks in advance.
UIImageView *photoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(20.0f, 42.0f, 280.0f, 280.0f)];
[photoImageView setBackgroundColor:[UIColor blackColor]];
[photoImageView setImage:self.image];
[photoImageView setContentMode:UIViewContentModeScaleAspectFit];
//Add overlay
UIImage *overlayGraphic = [UIImage imageNamed:#"chiu"];
UIImageView *overlayGraphicView = [[UIImageView alloc] initWithImage:overlayGraphic];
overlayGraphicView.frame = CGRectMake(30, 100, 260, 200);
[photoImageView addSubview:overlayGraphicView];
I'm not sure you can do this directly with just a UIImageView control. I think you're going to have to get into low-level drawing routines to get this done.
Option 1 (not recommended, just trying to answer the original question):
Have you tried placing the overlay UIImageView on top of the "main" UIImageView and setting its opacity to something less than 1 (say 0.4)? It's a crude hack, but it might get you somewhere.
Option 2 (probably the better path to travel):
Create an image context and then draw your "base" and "overlay" images on it. Then you'll have a UIImage you can output and will only need 1 UIImageView. Something like this (NOTE: this is a basic outline; you will need to add a LOT of time to get exactly what you want out of it!):
UIImage *baseImage = [UIImage imageNamed:#"base"];
UIImage *overlayImage = [UIImage imageNamed:#"overlay"];
UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);
CGContextDrawImage(context, rect, baseImage.CGImage);
CGContextDrawImage(context, rect, overlayImage.CGImage);
UIImage *combined = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I'm trying to get a UIImage to display at a certain spot on the screen. I've tried the initWithFrame in the code below, but it still displays at the top left corner. What should I do instead?
UIImage *headImage = [UIImage imageNamed:#"head"];
UIImageView *headView = [[[UIImageView alloc] initWithImage:headImage] initWithFrame:CGRectMake(200.0, 200.0, 400.0, 500.0)];
[self.view addSubview:headView];
Do the following: set the frame after you create the image view
UIImage *headImage = [UIImage imageNamed:#"head"];
UIImageView *headView = [[UIImageView alloc] initWithImage:headImage];
headView.frame = CGRectMake(200.0, 200.0, 400.0, 500.0);
[self.view addSubview:headView];
Cheers!
You cannot call init twice. Change your code to:
UIImage *headImage = [UIImage imageNamed:#"head"];
UIImageView *headView = [[[UIImageView alloc] initWithFrame:CGRectMake(200.0, 200.0, 400.0, 500.0)];
headView.image = headImage;
[self.view addSubview:headView];
The other answers are great if you want the image view to have a specific size. But if you want the image view's size to match the image's size as well as position the image view in a specific location then you should do:
UIImage *headImage = [UIImage imageNamed:#"head"];
UIImageView *headView = [[[UIImageView alloc] initWithImage:headImage]
CGRect frame = headView.bounds;
frame.origin.x = 200;
frame.origin.y = 400;
headView.frame = frame;
[self.view addSubview:headView];
This code has the benefit of working even if you change the size of the image.
If you don't need to change image size, then you can simple use setCenter method to move image on screen.
UIImage *headImage = [UIImage imageNamed:#"head"];
UIImageView *headView = [[UIImageView alloc] initWithImage:headImage];
[headView setCenter:CGPointMake(100, 100)]; // x, y
[self.view addSubview:headView];
I have an image that can be stretched in the horizontal direction, but need to be tiled vertically (one on top of the other) to fill my view. How could I do this?
I know that an image can be made resizable by using the -(UIImage *)resizableImageWithCapInsets:(UIEdgeInsets)capInsets method. So that works great for the horizontal direction but that method cannot be used for the vertical direction it really needs to be tiled, and not stretched in the vertical direction to work.
Now my idea of how this could work is to run a loop that creates a UIImageView with the horizontally stretched image, and simply adjust the frame.origin.y property, then add it as a subview each loop until I've gone past the height of the view. However this seems like an overly complex way of doing this and really not practical when the view need to be resized (on an iPad rotation for example)
Is there a simpler more efficient way of doing this on iOS 6.x?
I used this to tile an image horizontally in a UIImageView:
UIImage *image = [UIImage imageNamed:#"my_image"];
UIImage *tiledImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(0, 0, 0, 0) resizingMode:UIImageResizingModeTile];
UIImageView *imageView = [[UIImageView alloc] initWithImage:tiledImage];
This assumes you set the frame of the UIImageView to a larger size than your image asset. If the height is taller than your image, it will also tile vertically.
Have you considered using UIColor's method colorWithPatternImage: to create a repeating pattern and just pass an image with the correct horizontal width?
Code Example:
// On your desired View Controller
- (void)viewDidLoad
{
[super viewDidLoad];
UIImage *patternImage = [UIImage imageNamed:#"repeating_pattern"];
self.view.backgroundColor = [UIColor colorWithPatternImage:patternImage];
// ... do your other stuff here...
}
UIImage *image = [UIImage imageNamed:#"test.png"];
UIImage *resizableImage = [image resizableImageWithCapInsets:UIEdgeInsetsMake(0, image.size.width / 2, 0, image.size.width / 2)];
So I've figured out a solution for this. It is I think a little jacky but it works.
Basically what I've done was to create my stretchable image with the left and right caps specified. And then I initialize a UIImageView with it. I then adjust the frame of the image view to the desired width. This will appropriately resize the image that is contained within it.
Then finally I used a piece of code I found that creates a new image by vertically tiling the adjusted image that is now contained in the image view. Notice how instead of accessing the original UIImage I am using the UIImageView view's image property.
The last thing then is to set the new UIImage as the patterned background colour of the view
Here is the code:
// create a strechable image
UIImage *image = [[UIImage imageNamed:#"progressBackgroundTop#2x.png"] resizableImageWithCapInsets:UIEdgeInsetsMake(0, 60, 0, 60)];
// create the image view that will contain it
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
CGRect frame = imageView.frame;
frame.size.width = self.view.bounds.size.width;
imageView.frame = frame;
// create the tiled image
CGSize imageViewSize = imageView.bounds.size;
UIGraphicsBeginImageContext(imageViewSize);
CGContextRef imageContext = UIGraphicsGetCurrentContext();
CGContextDrawTiledImage(imageContext, (CGRect){ CGPointZero, imageViewSize }, imageView.image.CGImage);
UIImage *finishedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.view.backgroundColor = [UIColor colorWithPatternImage:finishedImage];