Loading UIImage from NSData causing jerky UITableView scrolling - ios

I am having getting my tableview to scroll smoothly when loading in images from NSData.
Some background ... I'm storing image NSData in Core Data (using the 'allows external storage' option so i dont think storing the URLs or something like that is the solution here). Further, I am storing two types of data, one high res with UIImagePNGRepresentation, and one low res with UIImageJPGRepresentation. I am loading in the low res images for my table view. The image property on the entry object is not stored in core data, its set after the fact. Here is how I am doing it:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView dequeueReusableCellWithIdentifier:#"LogCell" forIndexPath:indexPath];
[cell setGradients:#[[UIColor whiteColor], [UIColor lightTextColor]]];
Entry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
UIImage *entryImage = entry.image;
if (entryImage)
cell.rightImageView.image = entryImage;
else {
NSOperationQueue *oQueue = [[NSOperationQueue alloc] init];
[oQueue addOperationWithBlock:^(void) {
UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
if (!image) image = [UIImage imageNamed:#"defaultImage.png"];
entry.image = image;
}];
[oQueue addOperationWithBlock:^(void) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.rightImageView.image = entry.image;
}];
}];
}
return cell;
}
Any ideas on how to get this to run more smoothly? Any help is greatly appreciated!
Thanks
UPDATE
This has gotten me much closer, but there is still a little bit of jerkiness . . .
(within cellForRowAtIndexPath)
UIImage *entryImage = entry.image;
if (entryImage)
cell.rightImageView.image = entryImage;
else {
[self.imageQueue addOperationWithBlock:^(void) {
UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
if (!image) image = [UIImage imageNamed:#"bills_moduleIcon.png"];
entry.image = image;
[entry.image preload];
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.rightImageView.image = entry.image;
}];
}];
}
that preload method is in a cateogory on UIImageView and looks like this:
- (void)preload
{
CGImageRef ref = self.CGImage;
size_t width = CGImageGetWidth(ref);
size_t height = CGImageGetHeight(ref);
CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, width * 4, space, kCGImageAlphaPremultipliedFirst);
CGColorSpaceRelease(space);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), ref);
CGContextRelease(context);
}
I think my next plan of attack is to mess around with the size of the images stored (as mentioned in the comments) or perhaps trying to not update the imageViews while the table is scrolling. Any thoughts on those two methods are much appreciated! ill check back in when i've done that
UPDATE 2
Many many thanks for everyone who helped me out with this! Here was the final solution (in cellForRowAtIndexPath:
if (entryImage)
cell.rightImageView.image = entryImage;
else {
[self.imageQueue addOperationWithBlock:^(void) {
UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
if (!image) image = [UIImage imageNamed:#"bills_moduleIcon.png"];
entry.image = [image scaleToSize:cell.rightImageView.frame.size];
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.rightImageView.image = entry.image;
}];
}];
}
adding a new category method to UIImage:
-(UIImage*)scaleToSize:(CGSize)size
{
UIGraphicsBeginImageContext(size);
[self drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage* scaledImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return scaledImage;
}
Scrolling is now perfectly smooth. Awesome

A couple of issues:
I'd want to see how much time these two lines are taking:
Entry *entry = [self.fetchedResultsController objectAtIndexPath:indexPath];
UIImage *entryImage = entry.image;
While I gather that entry.image is a cached image, I wouldn't be so confident that objectAtIndexPath:indexPath isn't actually retrieving the NSData from Core Data (which is a good portion of the performance issue in storing images in a local database rather than the file system). I'd be inclined to do this in the background queue.
I don't understand why you're adding two operations to oQueue (which could operate concurrently!). It should just be as follows, so you don't try to dispatch to main queue until the image is retrieved:
[oQueue addOperationWithBlock:^(void) {
UIImage *image = [UIImage imageWithData:entry.photo.lowResImageData];
if (!image) image = [UIImage imageNamed:#"defaultImage.png"];
entry.image = image;
[[NSOperationQueue mainQueue] addOperationWithBlock:^(void) {
DMLogTableViewCell *cell = (DMLogTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath];
cell.rightImageView.image = entry.image;
}];
}];
It's probably not material, but you really shouldn't create a new NSOperationQueue for each cell. You should define a class property that is a NSOperationQueue, initialize it in viewDidLoad, and then use that queue in cellForRowAtIndexPath. It's not material, I'm sure, but it's not worth the overhead of creating a queue for each row of your table. (And if you ever go to server-based image retrieval, using a single queue is very important so you can control the maxConcurrentOperationCount.)

UIImage wont actually do the decode until it needs to draw the image, so its actually happening on the main thread when you assign the image to the image view.
I have a UIImage category which will force the image to be decoded (which you should run on a background thread) here : https://github.com/tonymillion/TMDiskCache

Related

Take Screenshot of UIImageView and UILabel then Share it

I think I'm near to solving the problem. I've a UItableView with a prototype cell which looks like this:
The gray-scale background image you see is coming from server and the text is the overlay text which is a UILabel on it and will change on every image which is also in the server. So it's like two layers which I'm trying to send. Third red button is the button of share and say I select share this image in email this function will be called:
-(UIImage *)makeImageofCell:(NSUInteger)sender
{
HomeCell *cellObj;
cellObj.tag = sender;
UIGraphicsBeginImageContext(cellObj.homeImg.frame.size );
[cellObj.homeImg.layer renderInContext:UIGraphicsGetCurrentContext()];
[cellObj.overlayText.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
In -(void)shareButtonClicked:(UIButton*)sender I've written the code of sharing. Now in this function I want to send that image. Here is what I'm doing: UIImage *imageToShare = [self makeImageofCell:sender.tag]; which is calling the makeImageofCell function and assigning it's value to a UIImage. So I'm writing this to share that URL.
NSString *imageToShare = [NSString stringWithFormat:#"Hey! Check out this Image %#",imageToShare];
But all I'm getting is Image is: (null). Any idea how this can be achieved?
Please following code for the screen shot of UIWindow You just replace with you custom view or cell.
- (UIImage*)takeScreenshot
{
// get the key-window references
UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
// manipulate boundries of key-window
CGRect rect = [keyWindow bounds];
// create context using size
UIGraphicsBeginImageContextWithOptions(rect.size,YES,0.0f);
// get the context reference
CGContextRef context = UIGraphicsGetCurrentContext();
// render in context
[keyWindow.layer renderInContext:context];
// get the rendered image
UIImage *capturedScreen = UIGraphicsGetImageFromCurrentImageContext();
// complete context
UIGraphicsEndImageContext();
// return image
return takeScreenshot;
}
Edited
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UIButton *btnShareImage = (UIButton*)[cell1 viewWithTag:KTAGABSENTBUTTON];
[btnShareImage addTarget:self action:#selector(shareImageAction:) forControlEvents:UIControlEventTouchUpInside];
[btnShareImage setTitle:[NSString stringWithFormat:#"%li %li",(long)indexPath.section,(long)indexPath.row] forState:UIControlStateDisabled];
return cell;
}
-(void)shareImageAction:(UIButton*)sender{
NSString *str=[sender titleForState:UIControlStateDisabled];
NSArray *ar=[str componentsSeparatedByString:#" "];
NSIndexPath *indexPath=[NSIndexPath indexPathForRow:[[ar objectAtIndex:1] intValue] inSection:[[ar objectAtIndex:0] intValue]];
HomeCell *cellObj = (HomeCell*) [yourTableView cellForRowAtIndexPath:indexPath];
UIImage *imageToShare = [self makeImageofCell:cellObj];
}
-(UIImage *)makeImageofCell:(HomeCell*)cellObj
{
UIGraphicsBeginImageContext(cellObj.homeImg.frame.size );
[cellObj.homeImg.layer renderInContext:UIGraphicsGetCurrentContext()];
[cellObj.overlayText.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return viewImage;
}
May this help to you.

UIImageView has weird space to the left of the image in a tableview

I have a UITableView that I programatically add to my view. I then load the data from an API, and resize the images to fit it to the width of the current device, and accordingly adjust the height of the image to keep the aspect ration, I then use that height as the height of the cells for my table view.
Here is some code and a screenshot of the result:
This is the image resize code:
+ (UIImage*)imageWithImage:(UIImage *)image convertToWidth:(float)width {
float ratio = image.size.width / width;
float height = image.size.height / ratio;
CGSize size = CGSizeMake(width, height);
UIGraphicsBeginImageContext(size);
[image drawInRect:CGRectMake(0, 0, size.width, size.height)];
UIImage * newimage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newimage;
}
This is in the init method of my custom table view cell:
_imgPicture = [[UIImageView alloc] init];
_imgPicture.contentMode = UIViewContentModeScaleAspectFit;
[self.contentView addSubview:_imgPicture];
This is my cellForRowAtIndexPath
RecipeTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:#"recipe_cell"];
if(cell == nil)
{
cell = [[RecipeTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"recipe_cell"];
}
NSDictionary *dict = [recipeArray objectAtIndex:[indexPath row]];
cell.lblName.text = [dict valueForKey:#"name"];
if([#"data:image/jpg;base64,null" isEqualToString:[NSString stringWithFormat:#"%#,%#",[dict valueForKey:#"pictureType"], [dict valueForKey:#"picture"]]])
{
cell.imgPicture.image = defaultImage;
}
else
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"%#,%#",[dict valueForKey:#"pictureType"], [dict valueForKey:#"picture"]]];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *ret = [UIImage imageWithData:imageData];
CGRect screenRect = [[UIScreen mainScreen] bounds];
CGFloat screenWidth = screenRect.size.width;
cell.imageView.image = [ImageExtension imageWithImage:ret convertToWidth:screenWidth];
}
return cell;
I have tried forcing the UIImageView to be at origin 0, 0 by settings the frame, but that didn't help, I am now thinking that it might be the fact that the image gets resized, and then something in the UITableViewCell breaks, but have no idea what it could be. Some assistance with this will be much appreciated.
Also note I had all this as a storyboard, and it had the exact same result, then I thought let me attempt it programatically and getting the same results.
Change
cell.imageView.image
^^^^^^^^^
to
cell.imgPicture.image
^^^^^^^^^^
This will solve problem...
first u can ensure resizing of image by giving tableview any backgroundcolor
secondly, if using ios8 then see your constraits
thirdly, if any ios then check indentation of uitableviewcell

How to get rid of massive Memory leak with blurred image in uitableview cell

Somehow when I scroll to the bottom of my table (96 items only) I get 1 gb of memory usage (it increase for every cell that gets created. I have a table that has an image with a blurred image in front of it that is using a cropped version of the original image with text then on top of that. Pretty simple. I'm using the apple provided sample code for blurring images available here: https://github.com/iGriever/TWSReleaseNotesView/blob/master/TWSReleaseNotesView/UIImage%2BImageEffects.m
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"itemCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NSDictionary *foodItem = [self.foodItems objectAtIndex:indexPath.row];
// Set up the image view
UIImageView *imageView = (UIImageView *)[cell viewWithTag:1];
UIImage *foodImage = [UIImage imageNamed:[foodItem objectForKey:FOOD_IMAGE_FILE_KEY]];
[imageView setImage:foodImage];
[imageView setContentMode:UIViewContentModeScaleAspectFill];
// Set up the label
UILabel *labelView = (UILabel *)[cell viewWithTag:2];
[labelView setFont:[UIFont flatFontOfSize:20.0]];
labelView.text = #"Blah";
// Set up the image view
UIImageView *blurredView = (UIImageView *)[cell viewWithTag:3];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
UIImage *blurredImage = [[self cropForBlur:foodImage] applyBlurWithRadius:4
tintColor:[UIColor colorWithWhite:1.0 alpha:0.2]
saturationDeltaFactor:1.2
maskImage:nil];
dispatch_sync(dispatch_get_main_queue(), ^{
blurredView.image = blurredImage;
});
});
return cell;
}
*Note: I know it is most likely the blur (as opposed to my cropping) as it only happens when I do the blur. Also it's nothing to do with the async dispatch stuff as it still happens if I don't do that.
Yes I'm using ARC. Yes I'm using storyboards.
Here's cropForBlur:
- (UIImage *)cropForBlur:(UIImage *)originalImage
{
CGSize size = [originalImage size];
int startCroppingPosition = 100;
if (size.height > size.width) {
startCroppingPosition = size.height/2 + ((size.width / 320) * 45);
} else {
startCroppingPosition = size.height/2 + ((size.width / 320) * 45);
}
// WTF: Don't forget that the CGImageCreateWithImageInRect believes that
// the image is 180 rotated, so x and y are inverted, same for height and width.
CGRect cropRect = CGRectMake(0, startCroppingPosition, size.width, ((size.width/320) * 35));
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:(size.width/160) orientation:originalImage.imageOrientation];
CGImageRelease(imageRef);
return newImage;
}
I've also tried looking in Instruments but it will show that I'm using heaps of memory in total but the big chunks of memory don't show up in the breakdown. Weird.
Here's the bit that's saying I am using heaps of memory in the left bar.
Here's the allocations bit in Instruments. I don't see how they could match up. I haven't got zombies on or anything (unless there's somewhere other that in edit scheme to change that).
Here's the leaks Instruments view after scrolling down a bit. Doesn't seem to show any real leaks :S So confused.
See the solution from this link. The problem is the image has a different scale from your screen scale. The final output image can be very big.

UICollectionView scroll performance using AFNetworking to load images

I have read quite a few of the UICollectionView posts about poor scrolling, but none seem to directly apply or they are still unanswered.
I'm using AFNetworking to asynchronously load the images (95px squared) onto each cell and then when the images are scrolled into view again, the image is restored from cache (as verified by the response code given as 0 instead of 200).
Here's what I've tried:
Commented out weakCell.photoView.image = image; so the images aren't draw on screen and the scrolling was smoother (still stuttered a little during the HTTP get)
Removed all of the AFNetworking code from the cellForRowAtIndexPath method and the scrolling was much smoother (even with the custom cell shadows, etc. still being drawn on screen)
When I draw only the cell view (with the shadows) on screen, scrolling is very smooth for 100 cells. As soon as I start drawing the images on screen, scrolling is very poor on my device and it's even noticeable on the simulator. Instagram has very smooth scrolling for hundreds of cells on their profile view, so I'm trying to get close to their performance.
Are there any ways that I can improve any of my code below in order to improve scrolling performance?
Here is my cell code:
#import "PhotoGalleryCell.h"
#implementation PhotoGalleryCell
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Setup the background color, shadow, and border
self.backgroundColor = [UIColor colorWithWhite:0.25f alpha:1.0f];
self.layer.borderColor = [UIColor blackColor].CGColor;
self.layer.borderWidth = 0.5f;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowRadius = 3.0f;
self.layer.shadowOffset = CGSizeMake(0.0f, 2.0f);
self.layer.shadowOpacity = 0.5f;
// Make sure we rasterize for retina
self.layer.rasterizationScale = [UIScreen mainScreen].scale;
self.layer.shouldRasterize = YES;
// Add to the content view
self.photoView = [[UIImageView alloc] initWithFrame:self.bounds];
[self.contentView addSubview:self.photoView];
}
return self;
}
- (void)prepareForReuse
{
[super prepareForReuse];
self.photoView.image = nil;
self.largeImageURL = nil;
}
And here is my UICollectionView code:
#pragma mark - Collection View Delegates
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return [zePhotos count];
}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
PhotoGalleryCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kPGPhotoCellIdentifier forIndexPath:indexPath];
// Get a reference to the image dictionary
NSDictionary *photoDict = [[zePhotos objectAtIndex:indexPath.row] objectForKey:#"image"];
// Asynchronously set the thumbnail view
__weak PhotoGalleryCell *weakCell = cell;
NSString *thumbnailURL = [[photoDict objectForKey:#"thumbnail"] objectForKey:#"url"];
NSURLRequest *photoRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:thumbnailURL]];
[cell.photoView setImageWithURLRequest:photoRequest
placeholderImage:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
weakCell.photoView.image = image;
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Error retrieving thumbnail... %#", [error localizedDescription]);
}];
// Cache the large image URL in case they tap on this cell later
cell.largeImageURL = [[photoDict objectForKey:#"large"] objectForKey:#"url"];
return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
[self performSegueWithIdentifier:#"showPhotoDetail" sender:self];
}
You could try adding a shadowPath to your cell init, it should improve performance, that's the code I used on one of my project to add a rounded shadowPath (see the UIBezierPath methods for more choice)
self.layer.shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.frame.bounds
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(10, 10)].CGPath;
Moreover if I remember correctly AFNetworking doesn't resize the image returned from the server, so it could have an impact on the quality of your image (despite the scale method you added to the UIImageView), I recommend dispatching the returned image to resize it if you want as so :
CGSize targetSize = cell.photoView.bounds.size;
[cell.photoView setImageWithURLRequest:photoRequest
placeholderImage:nil
success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CGFloat imageHeight = image.size.height;
CGFloat imageWidth = image.size.width;
CGSize newSize = weakCell.imageView.bounds.size;
CGFloat scaleFactor = targetSize.width / imageWidth;
newSize.height = imageHeight * scaleFactor;
UIGraphicsBeginImageContextWithOptions(newSize, NO, 0.0);
[image drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];
UIImage *small = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(),^{
weakCell.photoView.image = small;
});
});
}
failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"Error retrieving thumbnail... %#", [error localizedDescription]);
}];
Code inspection looks good, though I bet it is the compositing of the shadow which is adding a good deal to the lag. The way you figure out exactly what is causing the delay is to use the Time Profiler tool in Instruments. Here are the docs from Apple.
The problem is when you scroll quickly you're starting up hundreds of network requests at the same time. If you have the image cached, display it immediately. If you don't, only start the download when the table view slows down.
You can use something like this:
//Properties or Instance Variables
NSDate *scrollDateBuffer;
CGPoint scrollOffsetBuffer;
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
NSTimeInterval secondsSinceLastScroll = [[NSDate date] timeIntervalSinceDate:scrollDateBuffer];
CGFloat distanceSinceLastScroll = fabsf(scrollView.contentOffset.y - scrollOffsetBuffer.y);
BOOL slow = (secondsSinceLastScroll > 0 && secondsSinceLastScroll < 0.02);
BOOL small = (distanceSinceLastScroll > 0 && distanceSinceLastScroll < 1);
if (slow && small) {
[self loadImagesForOnscreenRows];
}
scrollDateBuffer = [NSDate date];
scrollOffsetBuffer = scrollView.contentOffset;
}
You will want to call loadImagesForOnscreenRows in other methods, like when new data comes in, viewWillAppear, and scrollViewDidScrollToTop.
Here's an example implementation of loadImagesForOnscreenRows:
- (void)loadImagesForOnscreenRows
{
#try {
for (UITableViewCell *cell in self.tableView.visibleCells) {
// load your images
NSURLRequest *photoRequest = …;
if (photoRequest) {
[cell.photoView setImageWithURLRequest:…];
}
}
}
#catch (NSException *exception) {
NSLog(#"Exception when loading table cells: %#", exception);
}
}
I have this in a try/catch block because in my experience [UITableView -visibleCells] isn't reliable - it occasionally returns deallocated cells or cells without a superview. If you make sure this method is only called when the table is not scrolling quickly, it shouldn't impact scroll performance too much.
Also, note that the AFNetworking UIImageView category doesn't expose the cache object. You'll need to modify it slightly to check if you already have an image cached; this answer should point you in the right direction.

iOS - DrawRect performance issue

I am using -drawRect for the first time in an attempt to alternatively speed up a UITableView. However, the drawRect method seems to be slowing the table down quite largely.
Please can you tell me how I can improve the drawRect method below in order to speed up the table?
Edit---
In the drawRect method, I am writing two NSStrings to the cell's view, two UIImages and a drop shadow to both of the NSStrings and one of the UIImages.
One of the aforementioned images is downloaded asynchronously and then setNeedsDisplay is called to draw that UIImage to the screen. I believe that this could initially be the reason for the lag occurring.
- (void) drawRect:(CGRect) rect {
CGContextRef context = UIGraphicsGetCurrentContext();
[[UIColor clearColor] set];
CGContextFillRect(context, rect);
CGContextSaveGState(context);
CGContextSetShadow(context, CGSizeMake(1,1),1);
//draw text here
if (shouldDrawImage == YES) {
CGContextDrawImage(context, CGRectMake(10, 10, 40, 40), self.image.CGImage);
}
CGContextDrawImage(context, CGRectMake(self.frame.size.width - 16, 0, 16, self.frame.size.height), [UIImage imageNamed:#"right_bar_including_holes"].CGImage);
NSString *authorName = [[self.info objectForKey:#"user"] objectForKey:#"full_name"];
[RGB(219, 240, 73) set];
CGSize maximumLabelSize = CGSizeMake(self.frame.size.width - 10 - 55 - 16,9999);
CGSize authorsize = [authorName sizeWithFont:[UIFont boldSystemFontOfSize:15]
constrainedToSize:maximumLabelSize
lineBreakMode:UILineBreakModeWordWrap];
[authorName drawInRect:CGRectMake(60, 10, self.frame.size.width - 60, authorsize.height) withFont:[UIFont boldSystemFontOfSize:15]];
[RGB(249,249,249) set];
NSString *description = [self.info objectForKey:#"description"];
CGSize descriptionSize = [description sizeWithFont:[UIFont systemFontOfSize:14] constrainedToSize:maximumLabelSize lineBreakMode:UILineBreakModeWordWrap];
[description drawInRect:CGRectMake(60, authorsize.height + 15, descriptionSize.width, descriptionSize.height) withFont:[UIFont systemFontOfSize:14]];
CGContextRestoreGState(context);
}
- (NSString *) reuseIdentifier {
return NSStringFromClass([SlideCell class]);
}
- (void) updateCellInfo:(NSDictionary *)_info {
[self setInfo:_info];
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 1, 1)];
[iv setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[[self.info objectForKey:#"user"] objectForKey:#"avatar"]]] placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *_image) {
dispatch_async(dispatch_get_main_queue(), ^{
shouldDrawImage = YES;
self.image = [self roundedImage:_image];
[iv release];
[self setNeedsDisplay];
});
} failure:nil];
[self setNeedsDisplay];
[self setSelectionStyle:UITableViewCellSelectionStyleNone];
}
Yes - you run Instruments' Time Profile on your app to tell you exactly how much time is spent, and where. It will tell you if it is the image, string, shadow, or something else.
You should profile that code and see if image is the problem first.
I'm not sure how AFNetworking library (which you are using) works when downloading asynchronously the images.
If you know that image is the problem, I suspect that image needs to be rescaled in UIImageView when it's set. That could be the problem. You would need to rescale the UIImage you want to set to the UIImageView to the UIIImageView's frame so no autorescaling triggers. That's costly when scrolling.
You receive image and inmediately dispatch code to main thread. Potential rescaling could work 'under the hood'. I would change that method to:
UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, IMGSIZEX, IMGSIZEY)];
[iv setImageWithURLRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[[self.info objectForKey:#"user"] objectForKey:#"avatar"]]] placeholderImage:nil success:^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *_image) {
//Note you have to implement rescaleToFitSize
UIImage* rescaled = [_image rescaleToFitSize:iv.frame.size];
dispatch_async(dispatch_get_main_queue(), ^{
self.image = _image;
[iv release];
[self setNeedsDisplay];
});
} failure:^{
//Handle failure! You create mem. leak on failure
[iv release];
}];
As a side note, you don't handle failures in image download. You definitively should.
Stock UITableView is as efficient as it gets, provided you use it properly:
1. Recycle cells (dequeue)
2. Avoid transparency whenever posible (alpha blending DOES slow things down)
3. Configuration of reused cells doesn't take much processing time.
I don't think anyone can significantly improve on Apple's code by overriding drawRect...

Resources