I am using iPhone to get data from my web service.
Because I wan't to display loading view while getting data I invoke web service synchronous.
I have button which navigate to table view (that get data from ws):
Loading *loading = [[Loading alloc]init];
[[[UIApplication sharedApplication] keyWindow] addSubview:loading.view];
TableViewController *tableViewController = [[TableViewController alloc] init];
[self.navigationController pushViewController:tableViewController animated:YES];
[loading.view removeFromSuperview];
And I call web service in viewDidLoad of my view.
webData = [NSURLConnection sendSynchronousRequest:req
returningResponse:&response
error:&error];
NSString *xml= [[NSString alloc] initWithBytes:[webData mutableBytes]
length:[webData length]
encoding:NSUTF8StringEncoding];
// Parse XML etc.
I have tried to add loading view before I call web service (in viewDidLoad) but still doesn't work.
Any idea how to display loading view?
You should call the web service asynchronously. By doing it synchronously, you are blocking the main (UI) thread & it can't finish loading your view or make it appear (or animate things on the loading view), etc.
Related
I'm a bit confused. I have 2 classes, an app delegate and a view controller. In my app delegate I get some data via this method:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
[responseData release];
NSDictionary *results = [responseString JSONValue];
NSArray *allTweets = [results objectForKey:#"results"];
[viewController setTweets:allTweets];
[window addSubview:viewController.view];
[window makeKeyAndVisible];
}
and in my view controller I have a button that should reload the data...I've tried a few things such as
Twitter_SearchAppDelegate *appDelegate= (Twitter_SearchAppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate connectionDidFinishLoading];
but none worked...am I going about this in the wrong way? I just want to be able to call the method that loads the data in the first place from the app delegate to the view controller.
Any help is appreciated!
Yes, you are going in the wrong direction.
You are trying to call the NSURLConnection delegate method your self (wrong).
If you want to simply reload the JSON data, you should call in your App Delegates method that initiates the download request to force in the data reload!
first of all , if i'm not wrong , connectionDidFinishLoading is a delegate method which is triggered when you start the connection process.
have you test that your application enters connectionDidFinishLoading ?
in addition when you evaluate your array/dictionary as source to your tableview , did you call again ["your-table-view-name" reloadData] ?
hope this helps..
A brief over view of what I am trying to do.
I am using the tableView:didSelectRowAtIndexPath: method inside my UITableViewController subclass which is catching a row selection from that view like so...
//..... inside tableView:didSelectRowAtIndexPath:
if (indexPath.section == 0) {
//--- Get the subview ready for use
VehicleSearchResponseTableViewController *vehicleSearchResponseTableViewController = [[VehicleSearchResponseTableViewController alloc] initWithNibName:#"VehicleSearchResponseTableViewController" bundle:nil];
// ...
//--- Sets the back button for the new view that loads
self.navigationItem.backBarButtonItem = [[[UIBarButtonItem alloc] initWithTitle:#"Back" style: UIBarButtonItemStyleBordered target:nil action:nil] autorelease];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:vehicleSearchResponseTableViewController animated:YES];
if(indexPath.row == 0) {
vehicleSearchResponseTableViewController.title = #"Mans";
EngineRequests *engineRequest = [[EngineRequests alloc] init];
[engineRequest getMans];
[engineRequest release];
}
if(indexPath.row == 1) {
//.... etc etc
As you can see in this method I set up a few things, pushing the new view onto the viewstack and changing the back buttons text, then I go into catching the different rows and then initiating a method in a subclass of nsobject where I want to have all my connection/request stuff going on.
Inside my NSObject I have several different methods for the different cells that you can select on the UITableViewController, basicly they specify different strings that will then initialize my ASIHTTPRequest wrapper to make a connection to the php script and catch all the data that will come back from the database.. NSObject looks like this.
//.... NSObject.m
- (IBAction) getMans
{
NSString *mansString = [[NSString alloc] initWithString:#"mans.php"];
[self grabURLInBackground:mansString];
[manusString release];
}
//....cont....
//--- Connect to server and send request ---------------->>
- (IBAction)grabURLInBackground:(NSString *)setUrlString
{
NSString *startURL = [NSString stringWithFormat:#"http://127.0.0.1:8888/CodeTest/%#", setUrlString];
NSURL *url = [NSURL URLWithString:startURL];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request
{
NSString *responseString = [request responseString]; //Pass request text from server over to NSString
NSData *responseData = [responseString dataUsingEncoding:NSUTF8StringEncoding]; //Create NSData object for Parser Delegate and load with responseString
NSLog(#"stuff %#",responseData);
}
- (void)requestFailed:(ASIHTTPRequest *)request
{
NSError *error = [request error];
NSLog(#"%#", error);
}
From here I would like to pass the data I am getting from the requestFinished method back over to the newly pushed UITableView.. However I have an error before I am able to get this far that I need to solve... if I run the simulator and click back and forth between the views (the main UITableViewController with the cells and then the newly popped view where I want to put the data) the application falls over and pops up an error in main.m Thread 1: program receive signal EXC_BAD_ACCESS.. I just don;t know whats causing because from what I can tell my code is not so bad.
Also when I debug my application I notice that once grabURLInBackground method has finished it bounces out back to the getMans method then goes back over to the UITableViewController and continues through the if statements, completely neglecting the requestFinished and requestFailed methods, and I just cannot figure out why.
I guess I am not sure if I am calling the methods and functions I need to use in the right places so if you have any suggestions or answers on how I can improve or if you know where my error is coming form that would be greatly appreciated.
There's a few issues with the code above but I'd guess that your bad access exception is due to the handling of your EngineRequests and use of AsiHttpRequest.
The code here
EngineRequests *engineRequest = [[EngineRequests alloc] init];
[engineRequest getMans];
[engineRequest release];
effectively creates an object then deallocates as soon as getMans has finished running.
Then inside the engineRequest object this code
[request setDelegate:self];
[request startAsynchronous];
requests that AsiHttpRequest notify the almost certainly released object once the request has completed.
There may be other issues at work here but I'd start by restructuring to try to keep this object around until at least after it's received the response from AsiHttpRequest.
Hard to tell from the brief overview, but generally when you bad_access and end up in the main application method, it's usually because you autoreleased something, then released it, and it craps out when the autorelease pool is drained. Might want to turn on NSZombiesEnabled and look for memory problems.
Who does receive your request?
The sender (and receiver) object is engineRequest.
But you release Engine Request in that very moment after you issued the async request (by mens of the getMans Method.
I would suggest that you
1. move the code
vehicleSearchResponseTableViewController.title = #"Mans";
EngineRequests *engineRequest = [[EngineRequests alloc] init];
[engineRequest getMans];
[engineRequest release];
from your UITableViewController's didSelectRowAtIndexPath method to your vehicleSearchResponseTableViewController's viewDidLoad method.
2. to retain your EngineRequests object and keep it in some instance variable within vehicleSearchResponseTableViewControllerand do not release it before the request is completely processed, either successfully or in error.
I have an iPhone app (in XCode 4) that uses the standard Tab Bar Application project template (UITabBarController).
My first view has a UITableView that gets data from a JSON feed. Here's where the controller loads that data:
#import "FirstViewController.h"
#import "SBJson.h"
#import "WheelRecord.h"
#import "WheelTableCell.h"
#import "UIImageView+WebCache.h"
#implementation FirstViewController
#synthesize wheels, wheelsTable;
- (NSMutableArray *) wheels {
if (!wheels) {
SBJsonParser *parser = [[SBJsonParser alloc] init];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://wheelspotting.com/recent.json"]];
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSArray *wheelData = [parser objectWithString:json_string error:nil];
self.wheels = [NSMutableArray array];
for (NSDictionary *wheel in wheelData)
{
WheelRecord *currentWheel = [[[WheelRecord alloc] init] autorelease];
currentWheel.title = [wheel objectForKey:#"title"];
currentWheel.smallImage = [wheel objectForKey:#"small_image"];
currentWheel.mediumImage = [wheel objectForKey:#"medium_image"];
currentWheel.largeImage = [wheel objectForKey:#"large_image"];
[self.wheels addObject:currentWheel];
}
}
return wheels;
}
I'm trying to reload that data any time the user selects a different tab and then goes back to the first tab.
I've tried setting wheels to nil in viewDidLoad and viewWillAppear but when I switch tabs and come back to the first tab, it never updates (I make changes to the data to test).
I don't believe that ViewDidLoad will be called again as all the view controllers will be present at all times. I would have thought that viewWillAppear should have been called though.
Silly question, but are you sure the data is not being updated? Perhaps it's just your table view that's not being updated with the new data? You are calling [yourTableView reloadData] somewhere from within your viewWillAppear or viewDidAppear method? Alternatively, pop a [yourTableView reloadData] just before the end of your if code, eg
[self.wheels addObject:currentWheel];
[yourTableView reloadData]; // where yourTableView is the pointer to your table view
}
Tab bar controllers can be awkward things to work with, I know I've personally spent many hours swearing at them. One thing to try, if you've subclassed UIViewController in any way, or are using a UITableViewController or UINavigationController make sure you've set the correct object type in the inspector for relevant tab - I've had no end of problems due to forgetting to do this in the past.
Have you tried [self reloadData]? Considering it's the UITableView FirstViewController you want to update.
I want load the html data in webview. At the button click, it open the viewcontroller and load the html data in this viewcontroller (add web view in this view controller using Interface builder). When the html data not proper loading and i press the back button, at that time crash the app. i am not doing allocation & init webview in the coding. set IBOUTLET using Interface builder & bind it.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
[connection release];
NSString *strResponce = [[NSString alloc] initWithData:jsonData_Info encoding:NSUTF8StringEncoding];
[jsonData_Info release];
NSError *error;
SBJSON *json = [[SBJSON new] autorelease];
self.jsonArray_Info=[json objectWithString:strResponce error:&error];
str_InfoDetail = [[self.jsonArray_Info objectAtIndex:0] valueForKey:#"Page"];
str_html = [NSString stringWithFormat:#"%#",str_InfoDetail];
NSString *temp;
temp = [NSString stringWithFormat:#"<html><head><style>body{background-color:transparent;}</style></head><body><span style='color:white'>%#</span></body></html>",str_html];
//web_Information = [[UIWebView alloc]init];
web_Information.backgroundColor=[UIColor clearColor];
web_Information.opaque= NO;
[web_Information loadHTMLString:temp baseURL:nil];
[act stopAnimating];
[strResponce release];
}
please give me any solution.
thanks.
Pls post some code and crash log if you need answers. By the look of it I think it may be because of implementation of UIWebView delegate in your class. I think when you navigate back you do not make the delegate nil which can cause the app to crash
I have a strange issue, when it comes to parsing XML with NSXMLParser on the iPhone. When starting the app, I want to preload 4 table-views, that are populated by RSS-Feeds in the background.
When I init the table-views one-by-one, than loading, parsing and displaying all works like a charm. But when I try to init all view at once (at the same time), than it seems, that the XML-parser-instances are disturbing each other. Somehow data from one XML-Feed are "broadcasted" into other xml-parser instances, where they do not belong. Example: there is a "teammember" item, with "This is my name". When this bug occurs, there is a string from another xml-feed added, i.e. resulting in: "This is my name58", where 58 is the chart-position of something from the other view. "58" seems to miss then on the other instance.
It looks to me, that this bug occurs because of the NSXMLParser-delegate method:
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
if (!currentStringValue) {
currentStringValue = [[NSMutableString alloc] initWithCapacity:50];
}
[currentStringValue appendString:string];
}
In this case "by coincidence" bytes are appended to strings, where they do not belong to.
The strange thing is, that every instance of NSXMLParser is unique, got its own unique delegates, that are attached to their own ViewController. Every parsing-requests spawns it own background-task, with its own (also also unique named) Autorelease-pool.
I am calling the NSXMLParser like this in the ViewController:
// prepare XML saving and parsing
currentStringValue = [[[NSMutableString alloc] initWithCapacity:50] retain];
charts = [[NSMutableArray alloc] init];
NSURL *url = [[NSURL alloc] initWithString:#"http://(SOME XML URL)"];
xmlParser = [[[NSXMLParser alloc] initWithContentsOfURL:url] retain];
//Set delegate
[xmlParser setDelegate:self];
//loading indicator
progressWheel = [[[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(150.0,170.0,20.0,20.0)] autorelease];
progressWheel.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;
[self.view addSubview:progressWheel];
[progressWheel startAnimating];
// start loading and parsing the xml-feed in the background
//[self performSelectorInBackground:#selector(parse:) withObject:xmlParser]; -> I also tried this
[NSThread detachNewThreadSelector:#selector(parse:) toTarget:self withObject:xmlParser];
And this is one of the background-tasks, parsing the feed:
-(void)parse:(NSXMLParser*)myParser {
NSAutoreleasePool *schedulePool = [[NSAutoreleasePool alloc] init];
BOOL success = [myParser parse];
if(success) {
NSLog(#"No Errors. xmlParser got: %#", myParser);
(POST-PROCESSING DETAILS OF THE DATA RETURNED)
[self.tableView reloadData];
} else {
NSLog(#"Couldn't initalize XMLparser");
}
[progressWheel stopAnimating];
[schedulePool drain];
[myParser release];
}
What could cause this issue? Am I calling the background-task in the right way? Why is this bug approaching, since every XML-Parser got its own, unique instance?
You should not be updating UI elements (like progressWheel) from inside a background thread. UI updates should be done on the main thread.
Use -performSelectorOnMainThread:withObject:waitUntilDone: to update UI elements from within a background thread.
I've released an open source RSS/Atom Parser for iPhone and it makes reading and parsing web feeds extremely easy.
You can set it to download the data asynchronously, or you could run it in a background thread synchronously to collect the feed data.
Hope this helps!