Iam trying to cache images without using external libraries in UITableview.
But i didnt get any results for that. My images are loading from URL .i have more than 40 images and is there any way to cache rather than loading directly ??
am using following code for loading image form URL
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"cell";
cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[testingTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier]
;
}
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(concurrentQueue, ^{
data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
dispatch_async(dispatch_get_main_queue(), ^{
cell.img.image = [UIImage imageWithData:data];
});
});
return cell;
}
You can use NSCache, NSCache works like an NSMutableDictionary with the advantage that is thread safe, with also some auto removal policies
Depending on your requirements you can create a singleton instance of it:
#interface SharedCache : NSCache
+ (id)sharedInstance;
#end
#implementation SharedCache
- (void) emptyCache{
[self removeAllObjects];
}
-(void) dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self];
}
-(id) init {
self = [super init];
if (self) {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(emptyCache) name:UIApplicationDidReceiveMemoryWarningNotification object:[UIApplication sharedApplication]];
}
return self;
}
+(id)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
#end
Of course you should take care about check whether or not the image is already cached with a specified key.
Why wouldn't you use libraries? A very good one to use is UIImageView+AFNetworking from AFNetworking UIKit which loads an Image from a URL asynchronously and also caches your images.
example code:
[yourImageView setImageWithURL:youURL placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
Related
Is the below code, I am loading data using printerArray = [SMPort searchPrinter];. This an expensive operating and locks the ui. Is there a way to do this asynchronously so I can show a loading indicator and when it is done show the data?
//
// SearchPrinterViewController.m
// PHP POS
//
// Created by Chris Muench on 3/12/14.
// Copyright (c) 2014 PHP Point Of Sale. All rights reserved.
//
#import "PrintingViewController.h"
#import "StarIO/SMPort.h"
#import "PrinterFunctions.h"
#interface PrintingViewController ()
#end
#implementation PrintingViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
uitableview_printerList.dataSource = self;
uitableview_printerList.delegate = self;
//Expensive operation. Could take up to 3-5 seconds
printerArray = [SMPort searchPrinter];
}
- (void)viewDidUnload
{
[super viewDidUnload];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return printerArray.count + 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
if (indexPath.row < printerArray.count)
{
PortInfo *port = [printerArray objectAtIndex:indexPath.row];
cell.textLabel.text = port.modelName;
NSString *detailText = [NSString stringWithFormat:#"%#(%#)", port.portName, port.macAddress];
cell.detailTextLabel.text = detailText;
}
else if (indexPath.row == printerArray.count)
{
cell.textLabel.text = #"Back";
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (indexPath.row < printerArray.count)
{
PortInfo *portInfo = [printerArray objectAtIndex:indexPath.row];
[PrinterFunctions PrintPHPPOSDocumentWithPortName:portInfo.portName textToPrint:self.textToPrint portSettings:#""];
}
[self dismissViewControllerAnimated:YES completion:nil];
}
#end
// This sends the fetching operation on the background
// You can put a loading indicator HERE BEFORE the dispatch.
// This sends the fetching operation on the background
[SVProgressHUD showWithStatus:#"Finding Printers" maskType:SVProgressHUDMaskTypeBlack];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
printerArray = [SMPort searchPrinter];
// Here you send tell the main thread to reload the table
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
[uitableview_printerList reloadData];
});
});
There are of course tons of other solutions, but this seems to me the minimal one for your case.
EDIT:
One thing I forgot: in order to make the printerArray writeable in the block, you have to add the keyword __block in front when you declare it.
EDIT: I put the final working code. I didn't need to add __block for some reaosn.
You can use a second NSOperationQueue or GCD (Grand Central Dispatch). The GCD approach is described above. Here is the NSOperation queue approach:
NSOperationQueue *printQueue = [[NSOperationQueue alloc] init];
[printQueue addOperationWithBlock:^{
// Background work
NSArray * array = [SMPort searchPrinter];
// Update the UI (in the main queue)
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
printerArray = [array copy];
[uitableview_printerList reloadData];
}];
}];
Take a look at this video:
https://developer.apple.com/videos/wwdc/2012/index.php?id=211
and this tutorial:
http://www.raywenderlich.com/19788/how-to-use-nsoperations-and-nsoperationqueues
I am using a UIViewController (1st) that displays a list of comics where a user can select a comic to download.
The download takes place in another UIViewController (2nd) that has a tableView (with a custom download cell).
I have an NSMutableArray declared in the 2nd viewController with all the comic URLs and it's tableView gets loaded from this array.
The problem is:
Whenever I add a downloadable item to the list and open the 2nd viewController, the tableView gets loaded from the beginning and all the files start to download again.
I need a way so that I can add the files from the 1st viewController to 2nd viewController's tableView and the downloads should continue from the same stage as they were previously (and not from the start again)
Below is the code of how my DownloadCell works. Each cell is downloading multiple files. The first method is called while a cell is being created.
-(void)startDownload:(NSArray*)comicFiles
{
allComicFiles=comicFiles;
downloadedFiles=[[NSMutableArray alloc] init];
[self removeViews];
[self appendViewsToCell];
[loading startAnimating];
[self downloadFile:0];
}
Now, the second method is called to download the files asynchronously.
-(void)downloadFile: (int)index
{
if(index>=[allComicFiles count])
{
[self downloadComplete];
return;
}
SingleComicFile *comicFile=allComicFiles[index];
NSURL *url=comicFile.URL;
NSString *fileName=comicFile.FileName;
lblFileName.text=fileName;
NSString *applicationDocumentsDir =
[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *storePath = [applicationDocumentsDir stringByAppendingPathComponent:[NSString stringWithFormat:#"%#",fileName]];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:storePath]){
[self updateDownloadProgress:url];
[self downloadFile:index+1];
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_sync(dispatch_get_main_queue(), ^{
[data writeToFile:storePath atomically:TRUE];
[self updateDownloadProgress:url];
[self downloadFile:index+1];
});
});
}
The tablewviecontroller class has a mutable array which is basically a array of file array. It has a constructor which I call from the comic listing page to populate the array.
-(MyDownloadManager *)initFileQueue: (NSMutableArray *)withComicFileQueue
{
if(_fileArray==nil)
{
_fileArray= [[NSMutableArray alloc]init];
}
_fileArray=withComicFileQueue;
return self;
}
And I am binding the tableview like
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"Cell";
DownloadCell *cell = [tableView dequeueReusableCellWithIdentifier:nil];
if (cell == nil) {
cell = [[DownloadCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
NSArray *comicFiles= _fileArray[indexPath.row];
[cell startDownload:comicFiles];
return cell;
}
Maybe in your case the solution is to make shareViewController that will be initiated once (loadView, viewDidLoad will be called once). Also when you close shared loading view controller it is not reallocated and continues to download selected file. You can implement some delegate method to notify download is completed and so on. In this way you will get shared long-living downloader.
static DownloadViewController *sharedInstance = nil;
#implementation DownloadViewController
+ (id) sharedInstance
{
static dispatch_once_t once;
dispatch_once(&once, ^{
sharedInstance = [DownloadViewController new];
});
return sharedInstance;
}
I want to make a application which will display images into UICollectionView.
Images will be downloaded from server and then shows into collectionView.
I am using custom collectionView layout into xib file.
At a time, 20 images is receiving from server.
Problem: I can't show newly downloaded images into collectionView.
Here is my code:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
BOOL reloaded = NO;
static NSString *cellIdentifier = #"cvCell";
CVCell *cell = (CVCell *)[collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
NSMutableArray *data = [self.dataArray objectAtIndex:indexPath.section];
NSString *cellData = [data objectAtIndex:indexPath.row];
dispatch_queue_t queue = dispatch_queue_create("com.justTest.anotherSingleApplication", NULL);
dispatch_async(queue, ^{
//code to be executed in the background
NSString *imageName1 = [[NSString alloc]initWithFormat:#"http://www.abc.com/images/thumb/%#", cellData];
NSString *url_Img1 = imageName1;
UIImage *aImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:url_Img1]]];
dispatch_async(dispatch_get_main_queue(), ^{
//code to be executed on the main thread when background task is finished
[cell.cellImage setImage:aImage];
});
});
if (indexPath.row == self.imageArray.count - 1 && !reloaded) {
getOnScrollImages *getImage = [[getOnScrollImages alloc] init]; // class to get image name from server
NSMutableArray *astring = (NSMutableArray *)[getImage getImageNameFromServer:#"list" board:#"111" pin:#"122345"]; // method to get image name from server
[self setNewTestArray:astring]; //adding newly downloaded image name into array
reloaded = YES;
dispatch_async(dispatch_get_main_queue(), ^{
[self.collectionView reloadData];
});
}
return cell;
}
Any suggestion please?
NOTE: I am just starting developing iOS application, this may be a very silly question.
Use asynchronously fetch to get data from server and display it in collectionView
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
YourDataModel *model = self.dataArray[indexPath.row];
YourCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier forIndexPath:indexPath];
if ([self checkWhetherImageAlreadyExist]) {
[cell.imageView setImage:model.image];
} else {
//show placeholder to avoid nothing in your UI, or your user gets confused
[cell.imageView setImage:placeholderImage];
[self startDownloadImageForIndexPath:indexPath];
}
}
- (void)startDownloadImageForIndexPath:(NSIndexPath *)indexPath
{
//YourImageDownloader is a class to fetch data from server
//imageDownloadsInProgress is a NSMutableDictionary to record the download process, which can avoid repeat download
YourImageDownloader *downloader = [self.imageDownloadsInProgress objectForKey:indexPath];
if (downloader == nil) {
YourDataModel *model = self.dataArray[indexPath.row];
//configure downloader
downloader = [[YourImageDownloader alloc] init];
[downloader setURL:model.url];
[downloader setCompletionHandler:^{
//download the image to local, or you can pass the image to the block
model.image = [UIImage imageWithContentsOfFile:model.localPath];
YourCell *cell = [self.mCollectionView cellForItemAtIndexPath:indexPath];
[cell.imageView setImage:model.image];
//remove downloader from dictionary
[self.imageDownloadsInProgress removeObjectForKey:indexPath];
}];
//add downloader to dictionary
[self.imageDownloadsInProgress setObject:downloader forKey:indexPath];
//start download
[downloader startDownload];
}
}
Use a class to download the image. If you have many images in one collection view, you may consider to save these images to local in case of memory warning. if now many, just leave the image in memory and display it in your collection view.
the code followed is save the image to local and read image data from local when displaying.
in .h:
#import <Foundation/Foundation.h>
#interface PortraitDownloader : NSObject
#property (nonatomic, copy) NSString *portraitName;
#property (nonatomic, copy) void (^completionHandler)(void);
- (void)startDownload;
- (void)cancelDownload;
#end
in .m
#import "PortraitDownloader.h"
#import <CFNetwork/CFNetwork.h>
#import "NSString+ImagePath.h" // it's a category to get the image local path
#interface PortraitDownloader ()
#property (nonatomic, strong) NSMutableData *activeDownload;
#property (nonatomic, strong) NSURLConnection *portraitConnection;
#end
#implementation PortraitDownloader
- (void)startDownload
{
self.activeDownload = [NSMutableData data];
NSString *urlstr = [NSString serverPortraitPathWithPortrait:self.portraitName];
NSURL *url = [NSURL URLWithString:urlstr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.portraitConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)cancelDownload
{
[self.portraitConnection cancel];
self.portraitConnection = nil;
self.activeDownload = nil;
}
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[self.activeDownload appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// Clear the activeDownload property to allow later attempts
self.activeDownload = nil;
// Release the connection now that it's finished
self.portraitConnection = nil;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// save to local path
NSString *localSavePath = [NSString localPortraitPathWithPortrait:self.portraitName];
[self.activeDownload writeToFile:localSavePath atomically:YES];
self.activeDownload = nil;
// Release the connection now that it's finished
self.portraitConnection = nil;
// call our delegate and tell it that our icon is ready for display
if (self.completionHandler) {
self.completionHandler();
}
}
#end
if you want to leave your image in-memory, just modify the completion block as:
in .h
typedef void (^Completion_handle) (UIImage *image);
#interface PortraitDownloader : NSObject
#property (nonatomic, copy) Completion_handle myCompletionBlock;
and in .m
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// get image from data
UIImage *image = [UIImage imageWithData:self.activeDownload];
self.activeDownload = nil;
// Release the connection now that it's finished
self.portraitConnection = nil;
// call our delegate and tell it that our icon is ready for display
if (self.myCompletionBlock) {
self.myCompletionBlock(image);
}
}
and also modify methods startDownloadImageForIndexPath, save the image to your model to retain it
This method expects to have answers immediately:
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
when your code doesn't respond fast enough to it, the app will usually display nothing, or sometimes just crash (depending on what you've setup)
A common design pattern is to store the info that will be supplied to the collectionView in a class variable (it doesn't have to be a property, but it often times is). You always store SOMETHING in that variable, even if it is old or stale data.
Then you have the methods defined in the UICollectionViewDataSource protocol pull what they need directly from the class variables, with no delay.
Other methods can fetch and retrieve and sling updated data around, and once they finish you call reloadData: on the collectionView to update the interface.
assuming the asynchronous calls you are using are successfully retrieving data eventually, they are probably too slow for what the UICollectionViewDataSource protocol methods are expecting.
A suggestion for how to get started would be to move the code fetching your data to separate methods, and then stage the data in a class variable or two which the collectionView can reliably draw from.
You can try it with static data loaded into the bundle at first if you need, and then move into asynchronous pulls from the web too.
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
UICollectionViewCell *cell=[collectionView dequeueReusableCellWithReuseIdentifier:#"cellIdentifier" forIndexPath:indexPath];
UIImageView *imgView=[[UIImageView alloc]initWithImage:[UIImage imageNamed:#"profile_pic.png"]];
NSMutableDictionary *contactData=[NSMutableDictionary new];
contactData = [self.collectionData objectAtIndex:indexPath.row];
imgView.image=[contactData objectForKey:#"image"];
[cell addSubview:imgView];
return cell;
}
I am trying to load an image form the internet into a cell.
When I'm using a single row then it's not taking much time, but when I have more then 5 rows then it is blocking UI. How can I solve this?
- (UITableViewCell *)tableView:(UITableView *)theTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
In this method: I am using that Code:
NSURL *url = [NSURL URLWithString:upcImageLink];
NSData *data = [NSData dataWithContentsOfURL: url];
UIImage *imageObj = [[UIImage alloc] initWithData:data];
[iconImgVw setImage:imageObj];
If I understand correctly, you are currently, making sync calls to download the tableview cell image. Sync call takes time and your screen/UITableView becomes unresponsive to touch events. The technique to avoid this is called Lazy loading.
Use SDWebImage for lazy loading of tableview images. Usage is simple,
#import <SDWebImage/UIImageView+WebCache.h>
...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier] autorelease];
}
// Here we use the new provided setImageWithURL: method to load the web image
[cell.imageView setImageWithURL:[NSURL URLWithString:#"http://www.domain.com/path/to/image.jpg"]
placeholderImage:[UIImage imageNamed:#"placeholder.png"]];
cell.textLabel.text = #"My Text";
return cell;
}
Alternatively, you can also implement lazy loading of image on your own refering to the Apple sample code.
Hope that helps!
please try the following code by replacing url:
dispatch_async( dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^(void)
{
NSData * data = [[[NSData alloc] initWithContentsOfURL:URL] autorelease];
UIImage * image = [[[UIImage alloc] initWithData:data] autorelease];
dispatch_async( dispatch_get_main_queue(), ^(void){
if( image != nil )
{
[iconImgVw setImage:image];
} else {
//error
}
});
});
what im trying to do is parse an XML data and show it in my UITableView but the thing is it shows like after 2-3 seconds, im trying to include a UIActivityIndicator when the data loads and also im trying to include a gcd, but the thing is im new at this stuff so im really confuse on what to do or where do i posible put the gcd code.
that is my .m file.
#implementation ViewController
#synthesize listTableView;
dispatch_queue_t myQueue;
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [[xmlParser listPopulated]count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *CellIdentifier = #"CustomCell";
dataFileHolder *currentData = [[xmlParser listPopulated] objectAtIndex:indexPath.row];
CustomCellXMLClass *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil) {
cell = [[CustomCellXMLClass alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"cell"];
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"CustomCellXMLSample" owner:self options:nil];
cell = [nib objectAtIndex:0];
}
NSString *nameLabel = [currentData nameOfCat];
NSString *dataToCacheLabel = [myCache objectForKey:nameLabel];
if(nameLabel != nil){
dataToCacheLabel = [NSString stringWithString:nameLabel];
if (dataToCacheLabel != nil) {
[myCache setObject:dataToCacheLabel forKey:nameLabel];
[cell.nameLabel setText:dataToCacheLabel];
}
}
NSString *detailLabel = [currentData descriptionOfCat];
NSString *stringToCache = [myCache objectForKey:detailLabel];
if (detailLabel != nil) {
stringToCache = [NSString stringWithString:detailLabel];
if (stringToCache != nil) {
[myCache setObject:stringToCache forKey:detailLabel];
[cell.detailLabel setText:stringToCache];
}
}
NSString *imageURL = [currentData imageLink];
NSData *dataToCache;
if (imageURL != nil) {
dataToCache = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageURL]];
if (dataToCache != nil) {
[myCache setObject:dataToCache forKey:imageURL];
[cell.imageShow setImage:[UIImage imageWithData:dataToCache]];
}
else {
NSURL *imageURL = [NSURL URLWithString:#"http://i178.photobucket.com/albums/w255/ace003_album/190579604m.jpg"];
dataToCache = [NSData dataWithContentsOfURL:imageURL];
[myCache setObject:dataToCache forKey:imageURL];
[cell.imageShow setImage:[UIImage imageWithData:dataToCache]];
}
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 78;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section{
NSString *title = #"Sample View";
return title;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.activityIndicator startAnimating];
self.activityIndicator.hidesWhenStopped = YES;
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(myQueue, ^ {
xmlParser = [[XMLParser alloc]loadXMLByURL:#"http://www.irabwah.com/mobile/core.php?cat=0"];
[self.activityIndicator performSelectorOnMainThread:#selector(stopAnimating) withObject:nil waitUntilDone:YES];
});
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Depending on how XMLParser works your current CGD block is ok for that. You wouldn't usually use performSelectorOnMainThread to return the UI interaction to the main thread but that's just a common convention thing.
Most of your problem could be coming from dataWithContentsOfURL: which you're calling on the main thread in cellForRowAtIndexPath:. You might want to take a look at SDWebImage to help with that, but you could do it yourself with GCD too.
cellForRowAtIndexPath will not be called just because your XML finished loading. In your code block, I suggest before calling stopAnimating that you call reloadData on your UITableView; this will trigger a series of calls, first to numberOfRowsInSection and then to cellForRowAtIndexPath repeatedly. Try putting breakpoints in these functions so you can see the flow of execution.