Altenating images in image view using thread - ios

I have the following code to toggle between two images in a image view. I am not using GCD because this code is part of an already existing system that has been coded this way.
- (void)updateimage {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.fileno % 2 == 0) {
self.imageViewTest.image = [UIImage imageNamed:#"Image7.png"];
}
else {
self.imageViewTest.image = [UIImage imageNamed:#"Image8.png"];
}
[self.imageViewTest setNeedsDisplay];
self.filenolabel.text = [NSString stringWithFormat:#"%d", self.fileno];
});
}
- (void)calculateFileNo {
while (1) {
self.fileno ++;
sleep(1);
[self updateimage];
}
}
- (void)viewDidLoad {
[super viewDidLoad];
self.detectionThread = [[NSThread alloc]initWithTarget:self selector:#selector(calculateFileNo) object:nil] ;
[self.detectionThread setName:#"NewThread"];
[self.detectionThread start];
}
The filenolabel label displays the right no for each loop. But at times the images do not toggle properly. The same image is displayed for 2 or 3 iterations of the loop. I want the image to be toggled for each iteration. Please help. Thanks in advance

Increment place is a problem 'self.fileno ++;' -> move it to dispatcher.
- (void)updateimage {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.fileno % 2 == 0) {
self.imageViewTest.image = [UIImage imageNamed:#"Image7.png"];
}
else {
self.imageViewTest.image = [UIImage imageNamed:#"Image8.png"];
}
[self.imageViewTest setNeedsDisplay];
self.filenolabel.text = [NSString stringWithFormat:#"%d", self.fileno];
self.fileno ++;
});
}
- (void)calculateFileNo {
while (1) {
sleep(1);
[self updateimage];
}
}

Increase your file no inside the main thread.
self.fileno++;
You should only be calling setNeedsDisplay if you override drawRect in a subclass of UImageView which is basically a custom view drawing something on the screen, like lines, images, or shapes like a rectangle.
So you should call setNeedsDisplay when you make changes to few variables on which this drawing depends and for view to represent that change , you need to call this method which internally will give a call to drawRect and redraw the components.
When you change an imageView of image or make changes to any subview, you need call this method.
- (void)updateimage {
dispatch_async(dispatch_get_main_queue(), ^{
if (self.fileno % 2 == 0) {
self.imageView.image = [UIImage imageNamed:#"Image7.png"];
}
else {
self.imageView.image = [UIImage imageNamed:#"Image8.png"];
}
[self.imageView setNeedsDisplay];
self.fileno ++; // Increament your file no inside the main thread.
self.textView.text = [NSString stringWithFormat:#"%d", self.fileno];
});
}

Related

Changing instance variable bugs animation in UIView

I am having a problem while trying to make a custom "sliding" object from scratch. I have a view self.taskView which should either stick to the left or stick to the right. This is working fine until I try to change the state of an instance variable called self.task.
This is a (webm) animation of it working "correctly": working.webm
But this only works when I do NOT change the instance variable self.task.state
This is a (webm) animation of it NOT working "correctly": not_working.webm
So I know that it is the code that changes the state of the task that is bugging it because if I just comment out all the [self setState:BOOL] statements it works as seen in "working.webm".
Any tips or ideas are appreciated. But I am a novice so please keep it simple if it is possible.
// Code to move the green UIview right or left
-(IBAction)handlePan:(UIPanGestureRecognizer *)sender {
if (sender.state == UIGestureRecognizerStateBegan) {
self.origin = CGPointMake(self.taskView.frame.origin.x, self.taskView.frame.origin.y);
} else if (sender.state == UIGestureRecognizerStateChanged) {
CGPoint touchLocation = [sender locationInView:self];
CGPoint location = CGPointMake(touchLocation.x, (self.taskView.frame.size.height/2));
self.taskView.center = location;
} else if (sender.state == UIGestureRecognizerStateEnded) {
if (self.origin.x == 0) {
// Stick right
if (self.taskView.center.x > ((self.frame.size.width/10)*4)) {
[self setPosition:NO]; // This sets the position of the view
[self setState:NO]; // This code is bugging the "animation"
} else { // Else stick to the left
[self setPosition:YES];
[self setState:YES];
}
} else {
// Stick left
if (self.taskView.center.x < ((self.frame.size.width/10)*6)) {
[self setPosition:YES];
[self setState:YES];
} else { // Else stick right
[self setPosition:NO];
[self setState:NO];
}
}
/*[UIView animateWithDuration:0.2
delay:0.01
options:UIViewAnimationOptionAllowUserInteraction
animations:^
{
[self.taskView setFrame:position];
} completion:^(BOOL finished){
}];*/
} else if (sender.state == UIGestureRecognizerStateCancelled) {
}
}
// THIS METHOD IS BUGGING THE "ANIMATION"
- (void)setState:(BOOL)state {
self.task.state = [NSNumber numberWithBool:state];
}
I think the problem is that you're trying to run a time-consuming method on the main thread, and that's why your animations are choppy.
Your problem as you mentioned resides here:
- (void)setState:(BOOL)state {
self.task.state = [NSNumber numberWithBool:state];
}
This is a method that probably does something more than just changing a property (to my understanding from your comments this is a database object)
What you could do, is to update this object on a background thread:
- (void)setState:(BOOL)state {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{
self.task.state = [NSNumber numberWithBool:state];
});
}

How do I add and animate infinite number of objects coming from the bottom of the screen in random order in gradually increasing speeds?

I have 3 different sprites. I want to continuously animate these three different sprites entering from the bottom of the screen, going up increasing velocities as the number of objects coming in increases. Also, the three should come in random order.
So, I tried the following code to get any of the three sprites to come in any random order but it didn't work as expected:
int randomNumber;
for (int i = 1; i<500; i++) {
randomNumber = arc4random_uniform(3);
NSLog(#"Value of random Number %d" ,randomNumber);
if (randomNumber == 0) {
[self addRedBall];
SKAction *moveBall = [SKAction moveToY:CGRectGetMaxY(self.frame) duration:5];
dispatch_queue_t actionDispatchRed;
actionDispatchRed = dispatch_queue_create("com.redball.dispatch", NULL);
dispatch_async(actionDispatchRed, ^ { [redBall runAction:moveBall]; });
[NSThread sleepForTimeInterval:2]; //Time interval to wait before the next ball comes in.
}
else if (randomNumber == 1) {
[self addBlueBall];
dispatch_queue_t actionDispatchBlue;
SKAction *moveBall = [SKAction moveToY:CGRectGetMaxY(self.frame) duration:5];
actionDispatchBlue = dispatch_queue_create("com.blueball.dispatch", NULL);
dispatch_async(actionDispatchBlue, ^{ [blueBall runAction:moveBall]; });
[NSThread sleepForTimeInterval:2];
}
else if (randomNumber == 2) {
[self addGreenBall];
SKAction *moveBall = [SKAction moveToY:CGRectGetMaxY(self.frame) duration:5
];
dispatch_queue_t actionDispatchGreen;
actionDispatchGreen = dispatch_queue_create("com.greenball.dispatch", NULL);
dispatch_async(actionDispatchGreen,
^ { [greenBall runAction:moveBall]; } );
[NSThread sleepForTimeInterval:2];
}
}
When I use the [NSThread sleepForTimeInterval:2]; , the program does not go any further than the starting screen.
When I try to run the program without the sleepForTimeInterval, a huge number of sprites come in and overlap each other resulting in only one visible sprite.
Please help.
I would not recommend using [NSThread sleepForTimeInterval:2];
Instead trying using [self performSelector:#selector(addBall) withObject:nil afterDelay:2];
-(void)addBall
{
self.addedBall++;
if (self.addedBall > 500)
{
return;
}
randomNumber = arc4random_uniform(3);
if (randomNumber == 0) {
[self addRedBall];
SKAction *moveBall = [SKAction moveToY:CGRectGetMaxY(self.frame) duration:5];
dispatch_queue_t actionDispatchRed;
actionDispatchRed = dispatch_queue_create("com.redball.dispatch", NULL);
dispatch_async(actionDispatchRed, ^ { [redBall runAction:moveBall]; });
[self performSelector:#selector(addBall) withObject:nil afterDelay:2];
}
//other else if stuff
}
Also I am unsure why you are running your SKActions in a dispatch_queue and is likely not needed.
I hope that helps.

CCSprite not appearing on screen when in action/block

Hello I am trying to make a game in cocos2d-swift, what I am trying to get is when the user taps on the screen and then two CCSprites that are on the screen stop and remove them selfs from the scene. Once they remove the same exact sprites again show up but have a random chance to become 3 other sprites (all properties same except color and type). The problem is I have everything working the code is executing in the correct areas but the sprites are not appearing on the iphone.
Here is the main code to replace the existing sprites.
- (void)resetPerimeterShape {
[baseShape removeFromParent];
[innerShape removeFromParent];
id resetAction = [CCActionCallBlock actionWithBlock:^(void){
baseShape = [CCSprite spriteWithImageNamed:baseShapeShape];
baseShape.position = ccp(self.contentSizeInPoints.width/2,self.contentSizeInPoints.height/2);
baseShape.color = baseShapeColor;
baseShape.rotation = shapeZRotation;
[baseShape setScale:baseShapeSize];
[self addChild:baseShape];
innerShape = [CCSprite spriteWithImageNamed:innerShapeShape];
innerShape.position = ccp(self.contentSize.width/2,self.contentSize.height/2);
innerShape.color = innerShapeColor;
innerShape.rotation = shapeZRotation;
[innerShape setScale:baseShapeSize];
NSLog(#"starting");
//this is running and outputting so i am 100% sure this function is called.
[self addChild:innerShape];
NSLog(#"done");
}];
id reRunAction = [CCActionCallBlock actionWithBlock:^(void){
//endsscene wip
}];
[self runAction:[CCActionSequence actionWithArray:#[resetAction,reRunAction]]];
}
This is a snippet of the code where it is called.
else{
//[self runAction:self.sounds];
NSString *A = [self randomShape];
NSString *B = [self randomShape];
if ([A isEqualToString:B]) {
baseShapeShape = A;
innerShapeShape = A;
}
else {
baseShapeShape = #"SQUARE (1).png";
innerShapeShape = B;
}
innerShapeColor = [self randomColor];
baseShapeColor = [self randomColor];
shapeZRotation = [self randomRotation];
[self resetPerimeterShape];
nap = true;
}
Did you forget to run the action? For instance:
[self runAction:resetAction];
Found the Solution I was Scaleing the sprite by 0 so it basically didn't show up

UICollection View Scroll lag with SDWebImage

Background
I have searched around SO and apple forum. Quite a lot of people talked about performance of collection view cell with image. Most of them said it is lag on scroll since loading the image in the main thread.
By using SDWebImage, the images should be loading in separate thread. However, it is lag only in the landscape mode in the iPad simulator.
Problem description
In the portrait mode, the collection view load 3 cells for each row. And it has no lag or insignificant delay.
In the landscape mode, the collection view load 4 cells for each row. And it has obvious lag and drop in frame rate.
I have checked with instrument tools with the core animation. The frame rate drop to about 8fps when new cell appear. I am not sure which act bring me such a low performance for the collection view.
Hope there would be someone know the tricks part.
Here are the relate code
In The View Controller
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
ProductCollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"ProductViewCell" forIndexPath:indexPath];
Product *tmpProduct = (Product*)_ploader.loadedProduct[indexPath.row];
cell.product = tmpProduct;
if (cellShouldAnimate) {
cell.alpha = 0.0;
[UIView animateWithDuration:0.2
delay:0
options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction)
animations:^{
cell.alpha = 1.0;
} completion:nil];
}
if(indexPath.row >= _ploader.loadedProduct.count - ceil((LIMIT_COUNT * 0.3)))
{
[_ploader loadProductsWithCompleteBlock:^(NSError *error){
if (nil == error) {
cellShouldAnimate = NO;
[_collectionView reloadData];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
cellShouldAnimate = YES;
});
} else if (error.code != 1){
#ifdef DEBUG_MODE
ULog(#"Error.des : %#", error.description);
#else
CustomAlertView *alertView = [[CustomAlertView alloc]
initWithTitle:#"Connection Error"
message:#"Please retry."
buttonTitles:#[#"OK"]];
[alertView show];
#endif
}
}];
}
return cell;
}
PrepareForReuse in the collectionViewCell
- (void)prepareForReuse
{
[super prepareForReuse];
CGRect bounds = self.bounds;
[_thumbnailImgView sd_cancelCurrentImageLoad];
CGFloat labelsTotalHeight = bounds.size.height - _thumbnailImgView.frame.size.height;
CGFloat brandToImageOffset = 2.0;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
brandToImageOffset = 53.0;
}
CGFloat labelStartY = _thumbnailImgView.frame.size.height + _thumbnailImgView.frame.origin.y + brandToImageOffset;
CGFloat nameLblHeight = labelsTotalHeight * 0.46;
CGFloat priceLblHeight = labelsTotalHeight * 0.18;
_brandLbl.frame = (CGRect){{15, labelStartY}, {bounds.size.width - 30, nameLblHeight}};
CGFloat priceToNameOffset = 8.0;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
priceToNameOffset = 18.0;
}
_priceLbl.frame = (CGRect){{5, labelStartY + nameLblHeight - priceToNameOffset}, {bounds.size.width-10, priceLblHeight}};
[_spinner stopAnimating];
[_spinner removeFromSuperview];
_spinner = nil;
}
Override the setProduct method
- (void)setProduct:(Product *)product
{
_product = product;
_spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
_spinner.center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds));
[self addSubview:_spinner];
[_spinner startAnimating];
_spinner.hidesWhenStopped = YES;
// Add a spinner
__block UIActivityIndicatorView *tmpSpinner = _spinner;
__block UIImageView *tmpImgView = _thumbnailImgView;
ProductImage *thumbnailImage = _product.images[0];
[_thumbnailImgView sd_setImageWithURL:[NSURL URLWithString:thumbnailImage.mediumURL]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType, NSURL *imageURL) {
// dismiss the spinner
[tmpSpinner stopAnimating];
[tmpSpinner removeFromSuperview];
tmpSpinner = nil;
if (nil == error) {
// Resize the incoming images
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
CGFloat imageHeight = image.size.height;
CGFloat imageWidth = image.size.width;
CGSize newSize = tmpImgView.bounds.size;
CGFloat scaleFactor = newSize.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(),^{
tmpImgView.image = small;
});
});
if (cacheType == SDImageCacheTypeNone) {
tmpImgView.alpha = 0.0;
[UIView animateWithDuration:0.2
delay:0
options:(UIViewAnimationOptionCurveLinear | UIViewAnimationOptionAllowUserInteraction)
animations:^{
tmpImgView.alpha = 1.0;
} completion:nil];
}
} else {
// loading error
[tmpImgView setImage:[UIImage imageNamed:#"broken_image_small"]];
}
}];
_brandLbl.text = [_product.brand.name uppercaseString];
_nameLbl.text = _product.name;
[_nameLbl sizeToFit];
// Format the price
NSNumberFormatter * floatFormatter = [[NSNumberFormatter alloc] init];
[floatFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[floatFormatter setDecimalSeparator:#"."];
[floatFormatter setMaximumFractionDigits:2];
[floatFormatter setMinimumFractionDigits:0];
[floatFormatter setGroupingSeparator:#","];
_priceLbl.text = [NSString stringWithFormat:#"$%# USD", [floatFormatter stringFromNumber:_product.price]];
if (_product.salePrice.intValue > 0) {
NSString *rawStr = [NSString stringWithFormat:#"$%# $%# USD", [floatFormatter stringFromNumber:_product.price], [floatFormatter stringFromNumber:_product.salePrice]];
NSMutableAttributedString * string = [[NSMutableAttributedString alloc] initWithString:rawStr];
// Change all the text to red first
[string addAttribute:NSForegroundColorAttributeName
value:[UIColor colorWithRed:157/255.0 green:38/255.0 blue:29/255.0 alpha:1.0]
range:NSMakeRange(0,rawStr.length)];
// find the first space
NSRange firstSpace = [rawStr rangeOfString:#" "];
// Change from zero to space to gray color
[string addAttribute:NSForegroundColorAttributeName
value:_priceLbl.textColor
range:NSMakeRange(0, firstSpace.location)];
[string addAttribute:NSStrikethroughStyleAttributeName
value:#2
range:NSMakeRange(0, firstSpace.location)];
_priceLbl.attributedText = string;
}
}
SDWebImage is very admirable, but DLImageLoader is absolutely incredible, and a key piece of many big production apps
https://stackoverflow.com/a/19115912/294884
it's amazingly easy to use.
To avoid the skimming problem, basically just introduce a delay before bothering to start downloading the image. So, essentially like this...it's this simple
dispatch_after_secs_on_main(0.4, ^
{
if ( ! [urlWasThen isEqualToString:self.currentImage] )
{
// so in other words, in fact, after a short period of time,
// the user has indeed scrolled away from that item.
// (ie, the user is skimming)
// this item is now some "new" item so of course we don't
// bother loading "that old" item
// ie, we now know the user was simply skimming over that item.
// (just TBC in the preliminary clause above,
// since the image is already in cache,
// we'd just instantly load the image - even if the user is skimming)
// NSLog(#" --- --- --- --- --- --- too quick!");
return;
}
// a short time has passed, and indeed this cell is still "that" item
// the user is NOT skimming, SO we start loading the image.
//NSLog(#" --- not too quick ");
[DLImageLoader loadImageFromURL:urlWasThen
completed:^(NSError *error, NSData *imgData)
{
if (self == nil) return;
// some time has passed while the image was loading from the internet...
if ( ! [urlWasThen isEqualToString:self.currentImage] )
{
// note that this is the "normal" situation where the user has
// moved on from the image, so no need toload.
//
// in other words: in this case, not due to skimming,
// but because SO much time has passed,
// the user has moved on to some other part of the table.
// we pointlessly loaded the image from the internet! doh!
//NSLog(#" === === 'too late!' image load!");
return;
}
UIImage *image = [UIImage imageWithData:imgData];
self.someImage.image = image;
}];
});
That's the "incredibly easy" solution.
IMO, after vast experimentation, it actually works considerably better than the more complex solution of tracking when the scroll is skimming.
once again, DLImageLoader makes all this extremely easy https://stackoverflow.com/a/19115912/294884
Note that the section of code above is just the "usual" way you load an image inside a cell.
Here's typical code that would do that:
-(void)imageIsNow:(NSString *)imUrl
{
// call this routine o "set the image" on this cell.
// note that "image is now" is a better name than "set the image"
// Don't forget that cells very rapidly change contents, due to
// the cell reuse paradigm on iOS.
// this cell is being told that, the image to be displayed is now this image
// being aware of scrolling/skimming issues, cache issues, etc,
// utilise this information to apprporiately load/whatever the image.
self.someImage.image = nil; // that's UIImageView
self.currentImage = imUrl; // you need that string property
[self loadImageInASecIfItsTheSameAs:imUrl];
}
-(void)loadImageInASecIfItsTheSameAs:(NSString *)urlWasThen
{
// (note - at this point here the image may already be available
// in cache. if so, just display it. I have omitted that
// code for simplicity here.)
// so, right here, "possibly load with delay" the image
// exactly as shown in the code above .....
dispatch_after_secs_on_main(0.4, ^
...etc....
...etc....
}
Again this is all easily possible due to DLImageLoader which is amazing. It is an amazingly solid library.

UITableView async image loading - not laying out until scroll

My asynchronously loading images aren't loading in my initial cells when the tableView first loads. They only show after I scroll the cells off-screen and them scroll them on-screen again. So I've read a number of S.O. questions on this issue, and I feel like I'm doing everything right. There's a lot going on in this method as it's populating a UIView with a number of avatar images.
I'm retrieving the images in a background thread, and then when I'm setting the images I go back to the main thread. And then I'm calling this:
[correctCell setNeedsLayout];
It appears to me that I'm doing everything correct but it seems like setNeedsLayout just isn't getting called back on the main thread.
Here's the method which I'm calling from tableView:cellForRowAtIndexPath::
- (void) setPeopleAvatars:(NSArray*)people cell:(WSExploreTableViewCell*)cell indexPath:(NSIndexPath *) indexPath{
[cell clearPeopleAvatars];
// Dimensions
CGFloat maxAvatars = cell.peopleView.frame.size.width / (kWSExploreCellPeopleAvatarDim + kWSExploreCellPeopleAvatarPadding);
CGFloat peopleAvatarX = cell.peopleView.frame.size.width - kWSExploreCellConvoBubblePadding - kWSExploreCellPeopleAvatarDim;
CGFloat peopleAvatarY = cell.peopleView.frame.size.height/2 - kWSExploreCellPeopleAvatarDim/2;
__block CGFloat lastAvatarX;
__block NSUInteger numAvatars = 0;
// First set the convo bubble image
cell.convoImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:kWSExploreCellConvoBubbleIcon]];
cell.convoImageView.frame = CGRectMake(cell.peopleView.frame.size.width - 1.0f - cell.convoImageView.frame.size.width,
cell.peopleView.frame.size.height/2 - cell.convoImageView.frame.size.height/2,
cell.convoImageView.frame.size.width,
cell.convoImageView.frame.size.height);
[cell.peopleView addSubview:cell.convoImageView];
cell.convoImageView.hidden = YES;
if (people.count == 0) {
cell.convoImageView.hidden = NO;
}
[people enumerateObjectsUsingBlock:^(id person, NSUInteger idx, BOOL *stop) {
// Stop at maxAvatars
if (idx >= maxAvatars) {
*stop = YES;
return;
}
[WSDatabaseManager getSmallProfilePic:(PFUser*)person callback:^(UIImage *image, NSError *error) {
if (!error) {
dispatch_async(dispatch_get_main_queue(), ^{
WSExploreTableViewCell * correctCell = (WSExploreTableViewCell*)[self.tableView cellForRowAtIndexPath:indexPath];
WSProfileImageView* avatarImageView = [[WSProfileImageView alloc] initWithImage:image croppedToCircle:YES diameter:kWSExploreCellPosterAvatarDim];
[avatarImageView setBackgroundColor:[UIColor clearColor]];
[avatarImageView setOpaque:NO];
[correctCell.peopleView addSubview:avatarImageView];
// Layout
CGFloat totalAvatarWidth = kWSExploreCellPeopleAvatarDim + kWSExploreCellPeopleAvatarPadding;
lastAvatarX = peopleAvatarX - (numAvatars * totalAvatarWidth);
[avatarImageView setFrame:CGRectMake(lastAvatarX, peopleAvatarY, kWSExploreCellPeopleAvatarDim, kWSExploreCellPeopleAvatarDim)];
// Move the convoImageView
CGRect convoFrame = correctCell.convoImageView.frame;
convoFrame.origin.x = convoFrame.origin.x - totalAvatarWidth;
correctCell.convoImageView.frame = convoFrame;
correctCell.convoImageView.hidden = NO;
// Update Counter
numAvatars++;
[correctCell setNeedsLayout];
});
}
}];
}];
}
As you probably know, the cell gets redrawn when it first appears on screen. That's why you are seeing the cell update when they reappear. You can try force refreshing the cell once you are done updating it.
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation;
Try SDWebImage
It will take care of loading your images when they are retrieved and will cache also. All you have to do is import UIImageView+WebCache.h :
[convoImageView setImageWithURL:YOUR_IMAGE_URL_HERE
placeholderImage:[UIImage imageNamed:#"placeholder.png"]
completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) {
//do something in completion block
}];

Resources