It sounds a bit strange, but since i use a spinning wheel indicator, the lazy image load don't works for the first image views (these once that are shown in the first screen). If the user scrolls down all other Images in the TableView loading correctly by a lazy download.
The main problem is, that NSURLConnection didn't calls didReceiveData.
- (void)startDownload
{
self.activeDownload = [NSMutableData data];
BOOL firstCell = (self.indexPathInTableView.row==0 && self.indexPathInTableView.section==0);
if(firstCell){
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:newsContent.title_picture]] delegate:self];
NSLog(#"Get Title Pic %# (%#)",newsContent.title, newsContent.title_picture);
self.imageConnection = conn;
}else{
NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:
[NSURLRequest requestWithURL:
[NSURL URLWithString:newsContent.cover_picture]] delegate:self];
NSLog(#"Get Thumb Pic %# (%#)",newsContent.title, newsContent.cover_picture);
self.imageConnection = conn;
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
NSLog(#"[NewsPicture][connection]didReceiveData");
[self.activeDownload appendData:data];
}
Edit: Added didReceiveResponse
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"[NewsPicture][connection]didReceiveResponse");
}
I'll get the Log "Get Thumb Pic ... (...)" with a correct Url, but for the for the first 5 rows (they fills the screen of an iPhone 4) i don't get the Log "[NewsPicture][connection]didReceiveData".
This is the way how i call the Indicator:
// Spinning Wheel
HUD = [[MBProgressHUD alloc] initWithView:self.view];
HUD.tag = 1000;
[self.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"wird geladen";
HUD.minShowTime = 25;
HUD.dimBackground = YES;
[HUD show:true];
[HUD showWhileExecuting:#selector(doWhileLoadingNews) onTarget:self withObject:nil animated:NO];
and if i only call
[self doWhileLoadingNews];
at this place all works fine, but without in indicator for loading data.
How could i fix it? (I can post more Code oder Informations if you need)
Edit: I still couldn't fix it. Is it possible to catch the result in another way then calling the 'didReceiveData'?
Edit: Added didReceiveResponse but with the same result, didReceiveResponse is also not called.
Related
What are the main differences between the following two methods of fetching a UIImage from a URL? I recently switched from Method 1 to Method 2 in my app and seemed to experience a drastic increase in speed when I thought that, essentially, both methods were nearly the same in practice. Just trying to figure out why I saw such a speed increase.
Method 1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSData *imageData = [NSData dataWithContentsOfURL:self.imageURL];
dispatch_async(dispatch_get_main_queue(), ^{
self.image = [UIImage imageWithData:imageData];
});
});
Method 2
- (void)fetchImage
{
NSURLRequest *request = [NSURLRequest requestWithURL:self.imageURL];
self.imageData = [[NSMutableData alloc] init];
self.imageURLConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
if(connection == self.imageURLConnection)
{
[self.imageData appendData:data];
}
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
if(connection == self.imageURLConnection)
{
self.image = [UIImage imageWithData:self.imageData];
}
}
My best guess is that because for Method 1 the AsyncURLConnection class multithreads:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/* process downloaded data in Concurrent Queue */
dispatch_async(dispatch_get_main_queue(), ^{
/* update UI on Main Thread */
So, you may see degraded performance due to contention for shared resources.
On the other hand Method 2, is actually just a collection of methods which are implemented more like transaction processing.
There's probably also more to it.
Whenever I create an NSURLConnection in a class I have, it always connects to the first URL connected to by that class. It has an ivar conn that the NSURLConnection is stored in, and here is the method that connects:
-(void)getMoreProblems
{
problemsPage++;
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://projecteuler.net/problems;page=%d",problemsPage]];
NSURLRequest *req=[NSURLRequest requestWithURL:url];
NSLog(#"%p",conn);
conn=[[NSURLConnection alloc] initWithRequest:req delegate:self];
NSLog(#"%p",conn);
}
I have checked by NSLoging the URL's description and the Connection's pointer that they are different, as well as telling the UIApplication to load the URL in safari. As far as I can tell, It tries to load the right page. I also tried both POST and GET, but it didn't make a difference. What might be causing this?
EDIT FOR ANYONE LOOKING AT THIS WITH A SIMILAR PROBLEM:
My problem ended up being that I did not reinitialize the NSMutableData I stored the connection data in after each page loaded.
This isn't really an answer, but it's too long for a comment. I can't see anything wrong with the code that you posted. I pasted your code for getMoreProblems into a new project and added the delegate methods necessary to look at the results -- as far as I can tell it worked fine. I can see in the resulting string, the problem numbers starting with 1 on the first page I receive (from the first call to getMoreProblems) and starting with problem 51 on the second call to getMoreProblems. The only thing I added to your getMoreProblems method was the if-else clause at the end. HEre is the code I used:
#synthesize window = _window,receivedData;
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
problemsPage = 0;
[self getMoreProblems];
}
-(void)getMoreProblems {
problemsPage++;
NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:#"http://projecteuler.net/problems;page=%d",problemsPage]];
NSURLRequest *req=[NSURLRequest requestWithURL:url];
NSLog(#"%p",conn);
conn=[[NSURLConnection alloc] initWithRequest:req delegate:self];
NSLog(#"%p",conn);
if (conn) {
self.receivedData = [NSMutableData data];
} else {
NSLog(#"The Connection Failed");
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
NSLog(#"%#",response.URL);
[self.receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
NSLog(#"In connection:didReceiveData:");
[self.receivedData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"Succeeded! Received %lu bytes of data",[receivedData length]);
NSString *page = [[NSString alloc] initWithData:self.receivedData encoding:NSUTF8StringEncoding];
NSLog(#"%#",page);
[self performSelector:#selector(getMoreProblems) withObject:nil afterDelay:5];
}
So, I can't reproduce your problem -- I'm guessing it lies elsewhere in some code that you didn't post.
I am attempting to write a bit of code that checks the URL of a datasource, then populates an array with objects from that URL. It actually works well, but if there is a problem with the web connection or the address I want to populate the array with data from a bundled file. The issue I am having is that the connection didFailWithError method is never called. I tried passing a simple string but it does not call. I want the app to still function for people who are using ipod touch or are in airplane mode.
connection didReceiveResponse is working without issue.
This is what I'm working with.
- (void)loadListData{
NSLog(#"Loading data from sources");
NSURLRequest *listURLRequest = [NSURLRequest requestWithURL:integerPhoneListURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:1.0];
[[NSURLConnection alloc] initWithRequest:listURLRequest delegate:self];
if (!listConnectFail){
phoneListJSON =[NSData dataWithContentsOfURL:integerPhoneListURL];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:phoneListJSON waitUntilDone:YES];
} else {
//This will tell us if there is an error loading the file
NSLog(#"File not found on web init from file");
phoneListJSON =[NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:#"contactlist" ofType:#"json"]];
[self performSelectorOnMainThread:#selector(fetchedData:) withObject:phoneListJSON waitUntilDone:YES];
}
//Initialize the filtered list with array of customer objects. Based on original data
filteredList = [[NSMutableArray alloc] init];
for (NSDictionary *dict in phoneListOriginal) {
contact *single = [[contact alloc] init];
single.fName = [dict objectForKey:#"fName"];
single.lName = [dict objectForKey:#"lName"];
single.extension = [dict objectForKey:#"extension"];
single.title = [dict objectForKey:#"title"];
single.department = [dict objectForKey:#"department"];
single.cellNumber = [dict objectForKey:#"cellNumber"];
//NSLog(#"%#", single.lName);
[filteredList addObject:single];
}
NSLog(#"Array filteredLIst contains %d records",[filteredList count]); }
-(void) connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
listConnectFail = YES;
NSLog(#"Connection Failed, pulling from file"); }
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
listConnectFail = NO;
NSLog(#"Connection Succeeded, populating from API");
}
I know it is probably something stupid that I am not seeing, but I could use the help to see what I don't
Thanks in advance!
How did you confirm that your delegate did not receive the message? Did you check the log?
Your code seems to assume that 'listConnectFail' will be set immediately after the NSURLConnection's init is done, which is not necessarily the case.
[[NSURLConnection alloc] initWithRequest:listURLRequest delegate:self];
if (!listConnectFail){...}
The NSURLConnection documentation states that 'The delegate will receive delegate messages as the load progresses.'
However, I am not sure about the airplane mode, maybe this particular error can be detected synchronously.
I'm trying to simply create a MBProgressHUD, show it, start a synch or asynch request, push a new controller on UINavigationController, and remove said MBProgressHUD. However, the HUD does not appear until the data is received. There is only a flicker of the HUD. I tried doing this asynchronously, but that did the exact same thing. Any ideas?
ASIFormDataRequest* request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:#"occ_get_images_by_tag" forKey:#"action"];
[request setPostValue:[NSString stringWithFormat:#"%#", tag_id] forKey:#"tag_id"];
//HUD is a property of the UIViewController
HUD = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:HUD];
HUD.delegate = self;
//Tried messing with this, making a message and changing onTarget: to self
//didn't work
[HUD showWhileExecuting:#selector(startSynchronous) onTarget:request withObject:nil animated:YES];
[request startSynchronous];
NSError* error = [request error];
if(!error){
NSArray* tempArr = [[request responseString] objectFromJSONString];
// Do some stuff
[self.navigationController pushViewController:imtv animated:YES];
[HUD removeFromSuperview];
}else{
NSLog(#"%#", [error description]);
}
My understanding of how ASIHTTPRequest works is that things that happen before startSynchronous will actually happen before that message is called. Then, anything inside the if(!error) will happen after data is received and ready to be processed. It just seems like the callback isn't working at all.
Also if it's of any help, this all happens in tableView:didSelectRowAtIndexPath:.
You will not be able to use MBProgressHUD on the main thread of the application. If you want to get this working in your sample code, use the delegates of the ASIHTTPRequest and you will be able to see the MBProgressHUD
I'm working on my first JSON example in objective-c and came across this great tutorial that I'm trying to reproduce. Along the way I decided to push the JSON returned into my already working tableView (just to ensure I could do something w/ the data in the view).
- (void)viewDidLoad {
[super viewDidLoad];
responseData = [[NSMutableData data] retain];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.unpossible.com/misc/lucky_numbers.json"]];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSArray *luckyNumbers = [responseString JSONValue];
NSMutableString *text = [NSMutableString stringWithString:#"Nums "];
for (int i = 0; i < [luckyNumbers count]; i++)
[text appendFormat:#"%#", [luckyNumbers objectAtIndex:i]];
self.movies = [[NSArray alloc] initWithObjects:#"First", text, #"Last", nil];
}
What I've found is that when I set the array in "connectionDidFinishLoading" it shows up as nothing in the running application - yet if I set this directly in the "viewDidLoad" method with 3 simple string values it shows up fine.
When I debug the running application I see the JSON response and the string looks valid (no issues that I can see).
Is the datasource for my tableView already set in stone before this "connectionDidFinishLoading" method or did I miss something?
Your UITableView will call upon its DataSource for data once initially, presumably sometime after viewDidLoad. After that first load, it will only request data as it needs it (i.e. as you scroll to different cells.) If you want to make it refresh its contents when your data is ready (like after you've received your URL data), call [tableView reloadData].
My initial question was solved by this solution:
At the end of my "connectionDidFinishLoading" method I call a method on the appDelegate called "jsonFinished".
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
//do all the json work and set the array that I'm using as my datasource
self.movies = [[NSArray alloc] initWithObjects:#"First", "Last", nil];
[appDelegate jsonFinished]; //have the app delegate do the refresh call back
}
Then inside the appDelegate I simply provide an implementation for the "jsonFinished" method that does a refresh of the UITableView
- (void)jsonFinished
{
moviesController.refreshDisplay;
}
And in the "refreshDisplay" method I do the reloadData on the tableView
- (void)refreshDisplay
{
[moviesTableView reloadData];
}
And now after the data is loaded the appDelegate fires off the method that reloads the data for tableView