IOS/Objective-C: Code to Make Round Image in Custom Cell - ios

The following code is working fine to make a square image round when placed in cellForRowAtIndexPath.
However, when I move the code to a custom cell, every time the tableview changes, it spreads the circle all the way across the table row into a horizontal blur.
//following crops and rounds image
CGSize itemSize = CGSizeMake(40, 40);
UIGraphicsBeginImageContextWithOptions(itemSize, NO, UIScreen.mainScreen.scale);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[self.iconView.image drawInRect:imageRect];
self.iconView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
self.iconView.layer.masksToBounds=YES;
self.iconView.layer.cornerRadius=20.0;
self.iconView.frame = CGRectMake(20, 20, 500, 50);
// [cell.iconView.image drawInRect:imageRect];
// cell.iconView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
In custom cell, I have been placing this code after the following:
self.iconview.image = [UIImage imageNamed:#"test.jpg"];
Does anyone know how I might fix this and still use a custom cell? Thanks in advance for any suggestions.

The issue is what "when I move the code to a custom cell" means - which you do not explain. But the problem is that iconView itself is being resized - it is being made wider. If you don't want that to happen, give it different constraints so that that doesn't happen.

There is no need for you to start the image context and get all the way down into CoreGraphics.
I accomplish this by:
imageView.image = myUIImage
imageView.layer.cornerRadius = imageView.bounds.size.width / 2

There is simple way to make rounded image in custom cell:
imageView.layer.cornerRadius = imageDemo.frame.size.width / 2;
imageView.clipsToBounds = YES;
or follow this link

Related

IOS: Make round thumbnail of image for tableview

I have been using variations of the following code in cellforrowatindexpath to make thumbnails for a tableview. However, I believe this code is unnecessarily complicated and stretches some images incorrectly. Can anyone please suggest the right way (i.e. efficient, consistent and non distorting) to make a round thumbnail (about 40x40) for display in a tableview? Thank-you:
//poor code
CGSize itemSize = CGSizeMake(40, 40);
UIGraphicsBeginImageContextWithOptions(itemSize, NO, UIScreen.mainScreen.scale);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[cell.imageView.image drawInRect:imageRect];
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
cell.imageView.layer.masksToBounds=YES;
cell.imageView.layer.cornerRadius=20.0;
cell.imageView.frame = CGRectMake(20, 20, 500, 50);
//add image to right
return cell;
Note: Following approach from yah in this answer works for me in detail view but not in tableview controller cells
//does not work consistently. Sometimes images are square, sometimes round, sometimes football shaped
cell.imageView.layer.cornerRadius = cell.imageView.frame.size.width / 2;
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
cell.imageView.clipsToBounds = YES;

Should we scale image before setting it to a imageView in UITableView

I am displaying a list of images with names, with a chevron accessory to see the full size version of selected image.
I see that the scrolling performance is not super smooth. I think the reason is that the image I am assigning to the imageView of each cell is the full size image (which gets scaled down automatically before display).
Will the performance improve if I maintain a smaller image that is of "thumbnail" size? If yes, what is the best way to do this? I see some example in How to resize images in UITableViewCell?. Is the answer (reproduced below) that uses the approach below, good enough?
Also, any other tips to make the scrolling smoother?
CGSize itemSize = CGSizeMake(30, 30);
UIGraphicsBeginImageContext(itemSize);
CGRect imageRect = CGRectMake(30.0, 30.0, itemSize.width, itemSize.height);
[thumbnail drawInRect:imageRect];
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
Yes, scaling down image helps a lot. I created the method below to resize following (and call it on a background thread to keep main UI thread humming)
I also, found calling reloadData on only the row that changed helps using
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
Method used to downsize.
- (UIImage *) returnThumbnail:(UIImage*)image
{
CGSize itemSize = CGSizeMake(120, 90);
UIGraphicsBeginImageContext(itemSize);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[image drawInRect:imageRect];
UIImage *scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSLog(#"Returning scaled image %#",scaledImage) ;
return scaledImage ;
}

Low quality UIImage after clipping it to a circle

I am trying to clip a UIImage to make it circular, I am starting with a 140*140px image and then running this code:
//round the image
UIImageView *roundView = [[UIImageView alloc] initWithImage:smallImage];
UIGraphicsBeginImageContextWithOptions(roundView.bounds.size, NO, 1.0);
[[UIBezierPath bezierPathWithRoundedRect:roundView.bounds
cornerRadius:roundView.frame.size.width/2] addClip];
[smallImage drawInRect:roundView.bounds];
UIImage *finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
smallImage = finalImage;
//end round image
This works as desired but the quality is very low, the image looks fuzzy and the edges around the circle are jagged. I want to achieve them same affect as:
image.layer.cornerRadius = self.thumbnailView.frame.size.width / 2;
image.clipsToBounds = YES;
Not sure why the quality of the image is so low. Can someone give me some pointers please?
You might want to keep scale at 0.f, so it matches the device scale (retina / not retina).
UIGraphicsBeginImageContextWithOptions(roundView.bounds.size, NO, 0.f);
You can do also draw a circle like this:
UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0.f);
CGRect interiorBox = CGRectInset(rect, 0.f, 0.f);
UIBezierPath *bezierPath = [UIBezierPath bezierPathWithOvalInRect:insideBox];
[bezierPath addClip];
[image drawInRect:rect];
UIImage *outputImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You are passing the wrong scale in to UIGraphicsBeginImageContextWithOptions. You are passing 1. Use UIGraphicsBeginImageContextWithOptions(roundView.bounds.size, NO, [UIScreen mainScreen].scale);
Are you sure you actually need to do this to the actual image? Would it not be enough to just make the image view rounded?
You can do this very easily.
UIImageView *imageView = // your image view with the image
imageView.frame = CGRectMake(0, 0, 140, 140); // the size you want
imageView.clipsToBounds = YES;
imageView.contentMode = UIViewContentModeScaleAspectFill;
imageView.layer.cornerRadius = 70; // make the corner radius half of the size
This will display your image cropped into a circle by the image view.
The image remains the same. It is just the display of the image that is rounded.
It's much less expensive too (time and memory).
Doing this in a table will not slow it down. You only need to do this once for each of the dequeued cells. For reused cells you do not need to do this as it is done when the cell is first created.

UITableView and thumbnails

UITableView has UIImageView, all images has the same size. But when I starting scroll or selecting row, some images can change size. I cannot understand why. Or how to make an imageview of a fixed size in a table row?
Use UIGraphicsImageContext with the table view cell image view like :
UIImage *thumbNail = [UIImage imageNamed:[_imageArray objectAtIndex:indexPath.row]];
// Setting thumbnail image
CGSize itemSize = CGSizeMake(60, 40);// Sample image size
UIGraphicsBeginImageContext(itemSize);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[thumbNail drawInRect:imageRect];
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
You can do one thing.. Use custom cell and set imageView's frame there.

How to tile a UIImage vertically while only streching horizontally on iOS?

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];

Resources