Asynchronous download issue with UITableView & ASIHTTP - iphone

I try to load some image on my UITableView with ASIHTTP but I have some problems. First of all I read an xml file (with tbxml) and I save title, image path and description in a dictionary and then in an array, for parsing I use this code:
- (void)loadUnknownXML {
// Load and parse the books.xml file
tbxml = [TBXML tbxmlWithURL:[NSURL URLWithString:#"http://www.xxx.com"]];
// If TBXML found a root node, process element and iterate all children
if (tbxml.rootXMLElement){
[self traverseElement:tbxml.rootXMLElement];
}
}
- (void) traverseElement:(TBXMLElement *)element {
TBXMLElement *child = element->firstChild;
TBXMLElement *items = [TBXML childElementNamed:#"item" parentElement:child];
do{
if (items->firstChild) {
TBXMLElement *titolo = [TBXML childElementNamed:#"title" parentElement:items];
TBXMLElement *descrizione = [TBXML childElementNamed:#"description" parentElement:items];
//NSLog(#"Titolo: %# \n Descrizione: %#",[TBXML textForElement:titolo],[TBXML textForElement:descrizione]);
self.elemento = [[NSMutableDictionary alloc] init];
[self.elemento setObject:[TBXML textForElement:titolo] forKey:#"Titolo"];
NSString *indirizzoImmagine = [TBXML textForElement:descrizione];
NSRange rangeSRC = [indirizzoImmagine rangeOfString:#"src=\""];
indirizzoImmagine = [indirizzoImmagine substringFromIndex:NSMaxRange(rangeSRC)];
NSRange rangeAMP = [indirizzoImmagine rangeOfString:#"&amp"];
NSRange rangeWidth = [indirizzoImmagine rangeOfString:#"&width"];
if (rangeAMP.location != NSNotFound) {
indirizzoImmagine = [indirizzoImmagine substringToIndex:NSMaxRange(rangeAMP)];
}
else if (rangeWidth.location != NSNotFound){
indirizzoImmagine = [indirizzoImmagine substringToIndex:NSMaxRange(rangeWidth)];
}
indirizzoImmagine = [indirizzoImmagine stringByReplacingOccurrencesOfString:#"&amp" withString:#""];
indirizzoImmagine = [indirizzoImmagine stringByReplacingOccurrencesOfString:#"&width" withString:#""];
[self.elemento setObject:indirizzoImmagine forKey:#"IndirizzoImmagine"];
[self.elemento setObject:[TBXML textForElement:descrizione] forKey:#"Descrizione"];
[self.array addObject:self.elemento];
}
}
while ((items=items->nextSibling));
}
then I start with download
- (void) loadURL:(NSURL *)url index:(int)index
{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
ASIDownloadCache *cache = [[ASIDownloadCache alloc] init];
[request setDownloadCache:cache];
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
[request setCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy];
[cache setStoragePath:#"/Users/kikko/kikko/xxx"];
request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:index], #"index",
url, #"url", nil];
[request setDelegate:self];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
int index = [[request.userInfo valueForKey:#"index"] intValue];
ASIDownloadCache *cache = [[ASIDownloadCache alloc] init];
[cache setStoragePath:#"/Users/kikko/kikko/xxx"];
[request.userInfo valueForKey:#"url"];
if ([cache cachedResponseDataForURL:[request.userInfo valueForKey:#"url"]]==nil) {
NSLog(#"%#",[request.userInfo valueForKey:#"url"]);
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:index inSection:0];
NSArray* rows = [NSArray arrayWithObjects:indexPath, nil];
[table reloadRowsAtIndexPaths:rows withRowAnimation:UITableViewRowAnimationNone];
}
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
NSLog(#"Error: %#",error);
}
at the end I put the image inside cell.imageview
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.array.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
return 50;
}
-(NSString *) stringByStrippingHTML:(NSString *)stringa {
NSRange r;
NSString *str = stringa;
while ((r = [str rangeOfString:#"<[^>]+>" options:NSRegularExpressionSearch]).location != NSNotFound)
str = [str stringByReplacingCharactersInRange:r withString:#""];
return str;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"ItemCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
elemento = [array objectAtIndex:indexPath.row];
NSURL *indirizzoImmagine = [NSURL URLWithString:[elemento objectForKey:#"IndirizzoImmagine"]];
[self loadURL:indirizzoImmagine index:indexPath.row];
ASIDownloadCache *cache = [[ASIDownloadCache alloc] init];
[cache setStoragePath:#"/Users/kikko/kikko/xxx"];
dataImmagine = [cache cachedResponseDataForURL:indirizzoImmagine];
[cell.imageView setImage:[UIImage imageWithData:dataImmagine]];
cell.textLabel.text = [elemento objectForKey:#"Titolo"];
return cell;
}

There are a lot of ways to go about this, but the most minimally invasive way to your existing code is to attach a userinfo dictionary to your request object.
In your traverse method do this:
//...
while ((items=items->nextSibling));
for (int i = 0; i < [self.array count]; i++)
{
[arrayData addObject:[NSNull null]];
}
Then do this in your request methods
- (void) loadURL:(NSURL *)url index:(int)index
{
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadCache:[ASIDownloadCache sharedCache]];
[request setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
[request setCachePolicy:ASIOnlyLoadIfNotCachedCachePolicy];
request.userInfo = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:index], #"index", nil];
[request setDelegate:self];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
int index = [[request.userInfo valueForKey:#"index"] intValue];
[arrayData replaceObjectAtIndex:index withObject:responseData];
//EDIT below:
NSIndexPath* indexPath = [NSIndexPath indexPathForRow:index inSection:0];
NSArray* rows = [NSArray arrayWithObjects:indexPath, nil];
[self.tableView reloadRowsAtIndexPaths:rows withRowAnimation:UITableViewRowAnimationNone];
}
In your cell code, do this:
if ([arrayData objecAtIndex:indexPath.row] != [NSNull null]) {
UIImage *img = [UIImage imageWithData:[self.arrayData objectAtIndex:indexPath.row]];
imageView.image = img;
[cell.imageView setImage:img];
} else {
imageView.image = nil;
}

There is best example given by Apple to download and show images Asyn
https://developer.apple.com/library/ios/#samplecode/LazyTableImages/Introduction/Intro.html
Meanwhile i will check your code as well

Related

How to display array of JSON values in custom tableView cells

I want to pass values inside for(NSDictionary *jsonDictionary in myJsonArray) which I get in NSLog to [array addObject:[[SaveList alloc] initWithEmail:email withPhone:phone withDate:date withName:name]];
Code is here
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSError *error = nil;
NSURL *url = [NSURL URLWithString:#" http:// Some url "];
NSString *json = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:&error];
NSLog(#"\n\n JSON : %#, \n Error: %#", json, error);
if(!error)
{
NSData *jsonData = [json dataUsingEncoding:NSASCIIStringEncoding];
NSArray *myJsonArray = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
// NSDictionary *jsonDictionary = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
for(NSDictionary *jsonDictionary in myJsonArray)
{
NSLog(#"JSON Dictionary = %#", jsonDictionary);
NSString *name = jsonDictionary[#"Name"];
NSString *date = jsonDictionary[#"Date"];
NSString *email = jsonDictionary[#"Email"];
NSString *phone = jsonDictionary[#"Phone"];
NSLog(#"Name = %#", name);
NSLog(#"Date = %#", date);
NSLog(#"Email = %#", email);
NSLog(#"Phone = %#", phone);
}
}
});
//Table implementation
array = [[NSMutableArray alloc]init];
//**Get email, phone, date, name here**
[array addObject:[[SaveList alloc] initWithEmail:email withPhone:phone withDate:date withName:name]];
self.tableView.dataSource = self;
self.tableView.delegate = self;
Why don't you add the objects as you receive them? Since this block of code will be executed asynchronously you could prepare your array, set your tableview and then execute the block where you fill your array and refresh the tableview.
Something like this:
// Prepare your array
array = [NSMutableArray arrayWithCapacity:0];
// Set your tableview's datasource & delegate
self.tableView.dataSource = self;
self.tableView.delegate = self;
// Fetch data asynchronously
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSError *error = nil;
NSURL *url = [NSURL URLWithString:#" http:// Some url "];
NSString *json = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:&error];
NSLog(#"\n\n JSON : %#, \n Error: %#", json, error);
if(!error)
{
NSData *jsonData = [json dataUsingEncoding:NSASCIIStringEncoding];
NSArray *myJsonArray = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
NSMutableArray *tmp = [NSMutableArray arrayWithCapacity:myJsonArray.count];
for(NSDictionary *jsonDictionary in myJsonArray)
{
NSString *name = jsonDictionary[#"Name"];
NSString *date = jsonDictionary[#"Date"];
NSString *email = jsonDictionary[#"Email"];
NSString *phone = jsonDictionary[#"Phone"];
//**Get email, phone, date, name here**
[tmp addObject:[[SaveList alloc] initWithEmail:email
withPhone:phone
withDate:date
withName:name]];
}
// Reload your tableview
dispatch_sync(dispatch_get_main_queue(), ^{
array = tmp; // Or add them to your datasource array, whatever suits you...
[self.tableView reloadData];
});
}
});
set number of rows
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
[array count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
SavedList *savedList = [array objectAtIndex:indexPath.row];
cell.text = savedList.name;
}
return cell;
}
I hope it will be helpful.
// U need to reload table view data whenever you add new object in it. It does work like a thread.
dispatch_sync(dispatch_get_main_queue(), ^{
array = myJsonArray;
[self.tableView reloadData];

Using GDataXML to Parse Included XML File

This XML will be included in the app:
<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Directory</title>
<description>Directory of Members</description>
<language>en</language>
<item>
<LastName></LastName>
<FirstName></FirstName>
<Address></Address>
<Phone></Phone>
<Email></Email>
</item>
<item>
<LastName></LastName>
<FirstName></FirstName>
<Address></Address>
<Phone></Phone>
<Email></Email>
</item>
</channel>
</rss>
I would like to be able to parse the XML and store into an RSSEntry Class that I have done before with other XMLs. The issue is that the only other XMLs I have worked with have been located online, and I use ASIHttpRequest Classes as part of the method, and am unsure how to go about changing the code to make it fit. I currently use this code:
- (void)viewDidLoad {
[super viewDidLoad];
self.allEntries = [NSMutableArray array];
self.queue = [[[NSOperationQueue alloc] init] autorelease];
self.feeds = [NSArray arrayWithObjects:#"http://316apps.com/LakesideNews/feed/",
nil];
NSLog(#"%#", self.feeds);
[self refresh];
}
- (void)refresh {
for (NSString *feed in _feeds) {
NSURL *url = [NSURL URLWithString:feed];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[_queue addOperation:request];
}
}
- (void)parseRss:(GDataXMLElement *)rootElement entries:(NSMutableArray *)entries {
NSArray *channels = [rootElement elementsForName:#"channel"];
for (GDataXMLElement *channel in channels) {
NSString *blogTitle = [channel valueForChild:#"title"];
NSArray *items = [channel elementsForName:#"item"];
for (GDataXMLElement *item in items) {
NSString *articleTitle = [item valueForChild:#"title"];
NSString *articleUrl = [item valueForChild:#"guid"];
NSString *articleDateString = [item valueForChild:#"pubDate"];
NSDate *articleDate = [NSDate dateFromInternetDateTimeString:articleDateString formatHint:DateFormatHintRFC822];
NSString *articleImage = [item valueForChild:#"content:encoded"];
RSSEntry *entry = [[[RSSEntry alloc] initWithBlogTitle:blogTitle
articleTitle:articleTitle
articleUrl:articleUrl
articleDate:articleDate
articleImage:articleImage] autorelease];
[entries addObject:entry];
}
}
}
- (void)parseFeed:(GDataXMLElement *)rootElement entries:(NSMutableArray *)entries {
if ([rootElement.name compare:#"rss"] == NSOrderedSame) {
[self parseRss:rootElement entries:entries];
} else if ([rootElement.name compare:#"feed"] == NSOrderedSame) {
[self parseAtom:rootElement entries:entries];
} else {
NSLog(#"Unsupported root element: %#", rootElement.name);
}
}
- (void)requestFinished:(ASIHTTPRequest *)request {
[_queue addOperationWithBlock:^{
NSError *error;
GDataXMLDocument *doc = [[GDataXMLDocument alloc] initWithData:[request responseData]
options:0 error:&error];
if (doc == nil) {
NSLog(#"Failed to parse %#", request.url);
} else {
NSMutableArray *entries = [NSMutableArray array];
[self parseFeed:doc.rootElement entries:entries];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
for (RSSEntry *entry in entries) {
int insertIdx = [_allEntries indexForInsertingObject:entry sortedUsingBlock:^(id a, id b) {
RSSEntry *entry1 = (RSSEntry *) a;
RSSEntry *entry2 = (RSSEntry *) b;
return [entry1.articleDate compare:entry2.articleDate];
}];
[_allEntries insertObject:entry atIndex:insertIdx];
[self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:insertIdx inSection:0]]
withRowAnimation:UITableViewRowAnimationRight];
}
}];
}
}];
}
- (void)requestFailed:(ASIHTTPRequest *)request {
NSError *error = [request error];
NSLog(#"Error: %#", error);
[self refresh];
}
I know to change all the areas for Title to things like Family name, etc., I just don't know how to go about getting the GDataXML to begin parsing the nested XML.
Ray Wenderlich has a fantastic tutorial covering exactly what you are doing. I would start there. Here's the link.

IPhone - download asynchronous work, but load asynchronous don't

I'm building my custom cell for a table view. I'm trying to load an image from internet and for it, i'm using async download. The image is being downloaded, but it's not showing this image in my cell. I already tried to show in a normal view and it's working fine. It does work too if the image is already downloaded or if I roll the table view and show the cell again. Does anybody knows what's going on?
Code:
DownloadImageManager.m
-(id)initWithImageName:(NSString *)imageAddress{
self = [super initWithFrame:CGRectMake(10, 5, 100, 100)];
if (self){
self.urlString = imageAddress;
av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
av.frame = self.frame;
[av setBackgroundColor:[UIColor greenColor]];
[self addSubview:av];
[av startAnimating];
[self checkImage];
}
return self;
}
-(void)checkImage{
bool isImageOnSysten = [self isImageOnFileSystem];
if (isImageOnSysten) {
//If image is on the system, loads the image, it's working fine here
NSLog(#"CSantos: isImageOnSysten %# is on system", self.urlString);
} else {
//here is the problem:
[self downloadImage];
}
}
-(void)downloadImage{
NSURL *url = [NSURL URLWithString:self.urlString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setAllowCompressedResponse:YES];
[request setQueuePriority:NSOperationQueuePriorityLow];
[request setDidFinishSelector:#selector(requestFinished:)];
[request setDidFailSelector:#selector(requestFailed:)];
[request setTimeOutSeconds:25];
[request setNumberOfTimesToRetryOnTimeout:3];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
NSArray *words = [self.urlString componentsSeparatedByString:#"/"];
NSString *fileName = [words lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSError *error = nil;
[responseData writeToFile:writablePath options:NSDataWritingAtomic error:&error];
NSLog(#"Write returned error: %#", [error localizedDescription]);
[av stopAnimating];
[av removeFromSuperview];
}
CellForProgram.m
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {
textLabel = [[UILabel alloc]initWithFrame:CGRectMake(60, 31, 235, 40)] ;
[self.contentView addSubview:textLabel];
photo = [[UIImageView alloc] initWithFrame:CGRectMake(5, 5, 70, 70)];
[photo setBackgroundColor:[UIColor blueColor]];
photo.image = imagePhoto.image;
[self.contentView addSubview:photo];
}
return self
Cell Caller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
CellForProgram *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier] ;
if (cell == nil) {
cell = [[[CellForProgram alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [speaker objectAtIndex:indexPath.row];
DownloadImageManager *imageManager = [[DownloadImageManager alloc] initWithImageName:[images objectAtIndex:indexPath.row]];
[cell.photo setImage:imageManager.image];
return cell;
}
You're not working with the pointers correctly.
When you call [cell.photo setImage:imageManager.image]; and the image does not exists, you're pointing it to nil or to an random memory space.
You need to create a pointer to your cell on the DownloadImageManager class, so that you can update the cell when the image finishes downloading.
Here's what I recommend:
Create a property on DownloadImageManager that points to your custom UITableViewCell class
Do not set the image on the tableView:cellForRowAtIndexPath: selector. Instead, set it directly on the DownloadImageManager.
Here's a simple modification to your code:
Cell Caller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
CellForProgram *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier] ;
if (cell == nil) {
cell = [[[CellForProgram alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [speaker objectAtIndex:indexPath.row];
DownloadImageManager *imageManager = [[DownloadImageManager alloc] initWithImageName:[images objectAtIndex:indexPath.row] andCell:cell];
return cell;
}
DownloadImageManager.m
-(id)initWithImageName:(NSString *)imageAddress andCell:(CellForProgram*)cell{
self = [super initWithFrame:CGRectMake(10, 5, 100, 100)];
if (self){
self.urlString = imageAddress;
self.cell = cell;
av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
av.frame = self.frame;
[av setBackgroundColor:[UIColor greenColor]];
[self addSubview:av];
[av startAnimating];
[self checkImage];
}
return self;
}
-(void)checkImage{
bool isImageOnSysten = [self isImageOnFileSystem];
if (isImageOnSysten) {
//If image is on the system, loads the image, it's working fine here
NSLog(#"CSantos: isImageOnSysten %# is on system", self.urlString);
cell.photo = self.image;
} else {
//here is the problem:
[self downloadImage];
}
}
-(void)downloadImage{
NSURL *url = [NSURL URLWithString:self.urlString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setAllowCompressedResponse:YES];
[request setQueuePriority:NSOperationQueuePriorityLow];
[request setDidFinishSelector:#selector(requestFinished:)];
[request setDidFailSelector:#selector(requestFailed:)];
[request setTimeOutSeconds:25];
[request setNumberOfTimesToRetryOnTimeout:3];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
NSArray *words = [self.urlString componentsSeparatedByString:#"/"];
NSString *fileName = [words lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSError *error = nil;
[responseData writeToFile:writablePath options:NSDataWritingAtomic error:&error];
NSLog(#"Write returned error: %#", [error localizedDescription]);
[av stopAnimating];
[av removeFromSuperview];
cell.photo = self.image;
}
That should get you going. If you need any clarification, be sure to leave a comment and I'll answer shortly.
EDIT: As an alternative, implement an delegate method on the DownloadImageManager...
Add this to the DownloadImageManager.h:
#protocol DownloadImageManagerDelegate <NSObject>
#optional
- (void)DownloadFinished:(DownloadImageManager*)manager;
#end
Instead of the CellForProgram, use the DownloadImageManager protocol, with this constructor as example:
-(id)initWithImageName:(NSString *)imageAddress andDelegate:(DownloadImageManagerDelegate*)delegate
And change your implementation of requestFinished: like so:
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
NSArray *words = [self.urlString componentsSeparatedByString:#"/"];
NSString *fileName = [words lastObject];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writablePath = [documentsDirectory stringByAppendingPathComponent:fileName];
NSError *error = nil;
[responseData writeToFile:writablePath options:NSDataWritingAtomic error:&error];
NSLog(#"Write returned error: %#", [error localizedDescription]);
[av stopAnimating];
[av removeFromSuperview];
if ([delegate respondsToSelector:#selector(DownloadFinished:)]) {
[delegate DownloadFinished:self];
}
}
Then, make your cell implment the given protocol, like so:
- (void)DownloadFinished:(DownloadImageManager*)manager {
this.photo = manager.image;
}
This way you can keep your functionality on DownloadImageManager, as you want it.
I told I wouldn't need to do this kind of change on DownloadImageManager! But thanks for trying to help, it helped me in other stuff I was stucked!
CellForProgram.m
- (id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithFrame:frame reuseIdentifier:reuseIdentifier]) {
textLabel = [[UILabel alloc]initWithFrame:CGRectMake(60, 31, 235, 40)] ;
[self.contentView addSubview:textLabel];
imagePhoto = [[DownloadImageManager alloc] initWithImageName:imageAdress.text];
[self.contentView addSubview:imagePhoto];
}
return self
}
DownLoadImageManager.m: add this method
-(void)changeImage:(NSString *)newImage{
self.urlString = newImage;
[self checkImage];
}
Cell Caller
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
CellForProgram *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier] ;
if (cell == nil) {
cell = [[[CellForProgram alloc] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
cell.textLabel.text = [speaker objectAtIndex:indexPath.row];
[cell.imagePhoto changeImage:[images objectAtIndex:indexPath.row]];
return cell;
}

displaying JSON data in Tableview in iphone

below is the JSON i want to parse it in such a way that for e.g date 1st should all events in that section of table and 2nd date should show all related events in another section
I am parsing using below code but i am not getting required sequence
SBJsonParser *parser= [[SBJsonParser alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.krsconnect.no/community/api.html?method=bareListEventsByCategory&appid=620&category-selected=350&counties-selected=Vest-Agder,Aust-Agder"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSDictionary *object = [parser objectWithString:json_string error:nil];
NSArray *results = [parser objectWithString:json_string error:nil];
appDelegate.books1 = [[NSMutableArray alloc] init];
appDelegate.dates =[[NSMutableArray alloc]init];
for (int j=0;j<10; j++) {
NSDictionary *dictOne = [results objectAtIndex:j];
NSLog(#"%# - %#", [dictOne objectForKey:#"date"]);
Date *aDate = [[Date alloc] initWithDictionary:[results objectAtIndex:j]];
[appDelegate.dates addObject:aDate];
[aDate release];
}
for (int i=0; i<10; i++) {
NSDictionary *dictOne = [results objectAtIndex:i];
NSArray *activitiesArray = [dictOne objectForKey:#"events"];
NSDictionary *dictTwo = [activitiesArray objectAtIndex:i];
NSDictionary *eventDict=[dictTwo objectForKey:#"event"];
// NSLog(#"%# - %#", [dictOne objectForKey:#"date"]);
// NSLog(#"%# - %#", [dictTwo objectForKey:#"affectedDate"]);
// NSLog(#"%# - %#", [eventDict objectForKey:#"location"]);
NSInteger*date=[dictOne objectForKey:#"date"];
NSInteger*affectedDate=[dictTwo objectForKey:#"affectedDate"];
NSString*appId =[eventDict objectForKey:#"appId"];
NSString*eventId=[eventDict objectForKey:#"eventId"];
NSString*location=[eventDict objectForKey:#"location"];
NSString*municipality=[eventDict objectForKey:#"municipality"];
NSString*title=[eventDict objectForKey:#"title"];
Book1 *aBook=[[Book1 alloc] initWithDate:date affectedDate:affectedDate location:location municipality:municipality title:title];
[appDelegate.books1 addObject:aBook];
int count=[appDelegate.books1 count];
}
the json format is given below
http://www.krsconnect.no/community/api.html?method=bareListEventsByCategory&appid=620&category-selected=350&counties-selected=Vest-Agder,Aust-Agder
You need to aggregate your data in some different way.
Here is how I'd do that:
...
// why do you parse your json string two times?
//NSDictionary *object = [parser objectWithString:json_string error:nil];
NSArray *results = [parser objectWithString:json_string error:nil];
// You have memory leak here. I assume that books1 and dates are both properties with "retain" flag set.
//appDelegate.books1 = [[NSMutableArray alloc] init];
//appDelegate.dates =[[NSMutableArray alloc]init];
NSMutableArray *data = [NSMutableArray array]
self.data = data;
// check that what we've parsed is NSArray
if (results && [results isKindOfClass:[NSArray class]]) {
for (NSDictionary *sectionDict in results) {
if ([sectionDict isKindOfClass:[NSDictionary class]]) {
NSString *sectionTitle = [[sectionDict objectForKey:#"date"] description];
NSArray *events = [sectionDict objectForKey:#"events"];
if (date && events && [events isKindOfClass:[NSArray class]]) {
NSMutableArray *rows = [NSMutableArray arrayWithCapacity:[events count]];
for (NSDictionary *eventDict in events) {
if ([eventDict isKindOfClass:[NSDictionary class]]) {
[rows addObject:#"testRow"];
}
}
[data addObject:[NSDictionary dictionaryWithObjectsAndKeys: sectionTitle, #"section", rows, #"rows", nil]];
}
}
}
}
- (NSInteger) numberOfSectionsInTableView:(UITableView *)tblView {
return [data count];
}
- (NSInteger) tableView:(UITableView *)tblView numberOfRowsInSection:(NSInteger)section {
return [[[data objectAtIndex:section] objectForKey:#"rows"] count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return [[data objectAtIndex:section] objectForKey:#"section"];
}
- (UITableViewCell *) tableView:(UITableView *)tblView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellID = #"DefaultCell";
UITableViewCell *cell = (UITableViewCell *)[tblView dequeueReusableCellWithIdentifier:cellID];
if ( cell == nil ) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellID] autorelease];
}
cell.textLabel.text = [[[data objectAtIndex:indexPath.section] objectForKey:#"rows"] objectAtIndex:indexPath.row];
return cell;
}

iphone nsurlconnect, tableview and activity indicator

i've a method that perform a connection to retreive some data and popolate a tableview.
This method works great.
Now i'm launching this method in viewDidLoad with
[NSThread detachNewThreadSelector:#selector(connTre)
toTarget:self
withObject:nil];
i've create this other function:
- (void)initSpinner {
av = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(195.0, 8.0, 30.0, 30.0) ] autorelease];
av.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[av hidesWhenStopped];
[self.view addSubview:av];
}
(i've initialite this in viewDidLoad)
- (void)spinBegin {
[av startAnimating];
}
- (void)spinEnd {
[av stopAnimating];
}
where's the better place to start and stop my activityindicatorview?
I've try to start with
[self performSelectorOnMainThread:#selector(spinBegin)
withObject:nil
waitUntilDone:false];
Here's my pretty standard code for table datasource:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [listaOggetti count];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
NSDictionary *dictionary = [listaOggetti objectAtIndex:section];
NSArray *array = [dictionary objectForKey:#"Elementi"];
return [array count];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 30;
}
-(UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier] autorelease];
cell.selectionStyle = UITableViewCellStyleValue1 ;
}
NSInteger sectionRows = [tableView numberOfRowsInSection:[indexPath section]];
NSInteger row = [indexPath row];
// Configure the cell.
NSDictionary *dictionary = [listaOggetti objectAtIndex:indexPath.section];
NSArray *array = [dictionary objectForKey:#"Elementi"];
NSString *cellValue = [array objectAtIndex:indexPath.row];
if (row == 0){
cell.textLabel.text = cellValue;
cell.textAlignment = UITextAlignmentCenter;
cell.backgroundColor = [UIColor redColor];
cell.font = [UIFont systemFontOfSize:13];
cell.selectionStyle = UITableViewCellStyleValue1 ;
} else {
cell.textLabel.text = cellValue;
cell.textAlignment = UITextAlignmentCenter;
cell.backgroundColor = [UIColor whiteColor];
cell.selectionStyle = UITableViewCellStyleValue1 ;
}
return cell;
}
this is the method for get my data:
- (void)connTre {
NSThread *spinThread=[[NSThread alloc] initWithTarget:self
selector:#selector(startSpinning) object:nil];
[spinThread start];
NSError *error;
NSURLResponse *response;
NSData *dataReply;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL: [NSURL URLWithString: #"myloginurl"]];
[request setHTTPMethod: #"GET"];
dataReply = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
//message tre soglie ok
path = #"my_url_for_getting_data";
url = [NSURL URLWithString:path];
NSError *errors;
htmlString = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:&errors];
NSString *regexString = #"(?:\r\n|[\n\v\f\r\302\205\\p{Zl}\\p{Zp}])";
NSString *reg2 =#".+class=\"dettaglioSoglie.*";
NSString *reg3 =#"</table>";
NSString*reg4=#"<td><b>(.+) </b></td><td>(.+) </td><td>(.+) </td><td>(.+) </td>";
NSString *replaceWithString = #"$1";
NSString *replaceWithString1 = #"Effettuato $2";
NSString *replaceWithString2 = #"Rimanente $3";
NSString *replaceWithString3 = #"Totale $4";
if(htmlString){
NSArray *linesArray = [htmlString componentsSeparatedByRegex:regexString];
for(NSString *lineString in linesArray) {
if(lineString ==[lineString stringByMatching:reg2]) { print = YES;}
if (print == YES) {
if(lineString ==[lineString stringByMatching:reg4]) {
replace = [lineString stringByReplacingOccurrencesOfRegex:reg4 withString:replaceWithString];
replace1 = [lineString stringByReplacingOccurrencesOfRegex:reg4 withString:replaceWithString1];
replace2 = [lineString stringByReplacingOccurrencesOfRegex:reg4 withString:replaceWithString2];
replace3 = [lineString stringByReplacingOccurrencesOfRegex:reg4 withString:replaceWithString3];
//NSLog(#"%#\n%#\n%#\n%#\n",replace, replace1, replace2, replace3);
//sectionz = [NSMutableArray arrayWithObjects: replace, nil];
//NSMutableArray *voice = [NSMutableArray arrayWithObjects: replace, replace1, replace2, replace3, nil];
NSMutableArray *voice = [NSMutableArray arrayWithObjects: replace, replace1, replace2, replace3, nil];
NSDictionary *detVoice = [NSDictionary dictionaryWithObject:voice forKey:#"Elementi"];
[listaOggetti addObject:detVoice];
NSLog(#"%#", listaOggetti);
}
//NSLog(#"%#",listaDettaglioOggetti);
}
if (lineString ==[lineString stringByMatching:reg3]) { print = NO;}
}
} else {
NSLog(#"Error reading file '%#'", htmlString);
}
[av stopAnimating];
[spinThread release];
}
and this is how i've configure my spinning:
- (void)startSpinning {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
av = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(195.0, 8.0, 30.0, 30.0) ] autorelease];
av.activityIndicatorViewStyle = UIActivityIndicatorViewStyleWhiteLarge;
[av hidesWhenStopped];
[self.view addSubview:av];
[av startAnimating];
[pool release];
}
with no lucky: jobs were perform, i see with nslog my data, av start and stop but data were not populated in my table (i don't see empty table, i don't see any table).
if i don't perform my animation i get my right table with data.
Thank's.
I don't know this is the correct way or not but this works fine for me.
-(void) yourFunctionThatPopulatesTableView
{
NSThread *spinThread=[[NSThread alloc] initWithTarget:self selector:#selector(startSpinner) object:nil];
[spinThread start];
//Populate TableView
//Last Line of he function
[av stopAnimating];
[spinThread release];
}
-(void)startSpinner
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
CGRect frame = CGRectMake(195.0, 8.0, 30.0, 30.0);
av = [[UIActivityIndicatorView alloc] initWithFrame:frame];
av.activityIndicatorViewStyle=UIActivityIndicatorViewStyleGray;
[av hidesWhenStopped];
[self.view addSubview:av];
[av startAnimating];
[pool release];
}
Any Function called by thread should have a NSAutoReleasePool as per documentation.