I have an app which has UITabBar with 5 views, each attached to a different UIWebView. Each of the WebViews responds to:
webViewDidStartLoad:(UIWebView *)webView
webViewDidFinishLoad:(UIWebView *)webView
Those two are responsible for displaying a loading screen (separate image for each tab, toggled visible or not) and activity indicator.
It all works fine when the page is loaded to WebView. User taps a link on the page, loading image is displayed along with the activity indicator. When the page is loaded they both disappear and new website is presented.
The problem is when user taps on one of the TabBar items. App intercepts the event and launches a method in appropriate view, which is refreshing the page.
The problem: after the tap view changes immediately to the other WebView, however loading screen takes a long time to appear (from what I gather it only shows when the page passed to homeView starts loading) and I can't figure out why. Displaying loading items is the first thing the app should do after method is called.
Here is the code that calls a method from TabBar controller:
[hv performSelector:#selector(goToPage) withObject:nil afterDelay:0.0];
and here is the goToPage method:
- (void) goToPage
{
homeView.delegate = self;
self.showLoading;
NSURL *url = [NSURL URLWithString: [NSString stringWithFormat: #"%#/seed.php", appURL]];
NSString *body = [NSString stringWithFormat: #"uid=%#", uID];
NSData *data = [body dataUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
[request setHTTPMethod: #"POST"];
[request setHTTPBody: data];
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
[request release];
NSString *seedString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
NSArray *seedArray = [seedString componentsSeparatedByString:#":::"];
NSString *eventNumber = [[seedArray objectAtIndex:0] retain];
NSString *passedSid = [[seedArray objectAtIndex:1] retain];
if (!seedString) {
// can't connect
NSLog(#"Can't connect.");
}else {
// connected
NSLog(#"Events: %#", eventNumber);
if(![evtNo isEqualToString:eventNumber]){
evtNo = eventNumber;
if(![evtNo isEqualToString:#"0"]){
AudioServicesPlaySystemSound (kSystemSoundID_Vibrate);
}
}
}
NSURL *urlView = [NSURL URLWithString: [NSString stringWithFormat: #"%#/index.php", appURL]];
NSString *bodyView = [NSString stringWithFormat: #"sid=%#", passedSid];
NSData *dataView = [bodyView dataUsingEncoding:NSUTF8StringEncoding];
NSMutableURLRequest *requestView = [[NSMutableURLRequest alloc]initWithURL:urlView];
[requestView setHTTPMethod: #"POST"];
[requestView setHTTPBody: dataView];
[homeView loadRequest: requestView];
if([evtNo isEqualToString:#"0"]){
// clearing badge
[[[[[self tabBarController] viewControllers] objectAtIndex: 0] tabBarItem] setBadgeValue: nil];
}else{
[[[[[self tabBarController] viewControllers] objectAtIndex: 0] tabBarItem] setBadgeValue: evtNo];
}
}
The goal I have is to display the loading image the moment user taps the TabBar item and remain visible until page stops loading.
The solution to this issue was to launch the method goToPage without the delay, display loading screen in goToPage and move the time consuming bits into another method inside that class, launched using persormSelector.
Related
I am building an iOS app in which I want to allow people to order stuff from stores that are registered in my database. To allow my users to see products, companies, etcetera, I need to download array's with the requested information. This is how I do that:
- (void)getOrderItems {
_dbAction = #"getOrderItems";
NSString *post = [NSString stringWithFormat:#"controller=%#&action=%#&orderNumber=%#", #"BakkerFunctions", #"getOrderItems", self.orderNumber];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
NSURL *url = [NSURL URLWithString:#"http://www.mysite.nl/API/"];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Current-Type"];
[request setHTTPBody:postData];
[request setURL:url];
NSURLConnection *conn = [[NSURLConnection alloc]initWithRequest:request delegate:self];
if(conn)
{
NSLog(#"Connection Successful");
}
else
{
NSLog(#"Connection could not be made");
}
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData*)data {
NSArray *orderItemsFromDatabase = [[NSArray alloc] init];
orderItemsFromDatabase = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:NULL][#"itemsFromDatabase"];
NSString *datastring = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
NSLog(#"%# Array:%#", datastring, orderItemsFromDatabase);
//convert array from database in array of custom objects
NSMutableArray *orderItems = [[NSMutableArray alloc] init];
for (int i = 0; i < orderItemsFromDatabase.count; i++) {
[orderItems addObject:
[[Item alloc] initWithItemName:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemName"]
itemDescription:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemDescription"]
itemCode:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemCode"]
itemBarCode:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemBarCode"]
itemPrice:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemPrice"]
itemQuantity:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemQuantity"]
itemUserRemark:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemUserRemark"]
itemCompany:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"itemCompany"]
productImageNumber:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"productImageNumber"]
category:[[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"category"]
itemImage:[UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"http://www.imagine-app.nl/ProductImages/%#%#", [[orderItemsFromDatabase objectAtIndex:i]objectForKey:#"productImageNumber"], #".jpg"]]]]]];
}
self.orderItems = [NSArray arrayWithArray:orderItems];
}
the getOrderItems method is a method inside the custom class I have for each order. when the user selects an orders, this method gets called to download the products that belong to this order. Now this process works all fine, but the problem is that the data gets receiver when my TableView already set itself up.
I want to add an observer to check if the amount of products in this array changes, and if it does, i want to update the tableview. I have searched for hours on SO an google, but not much is said about this and nothing useful to me.
any help would be much appreciated! thank you in advance
A simple solution would be to use a MBProgressHUD or a similar control which will display a message saying that please wait while the data is being downloaded. Once, the data is downloaded it will trigger a callback meaning that it has downloaded the data and then you can refresh your UITableView to reflect all the data.
Override the orderItems setter and reload the table in there.
- (void)setOrderItems:(NSArray*)items
{
_items = items;
[self.tableView reloadData];
}
I'm not adding an observer anymore, As soon as my custom class finished processing the received data, it calls the orders view controller. That made that the data is loaded before the tableview is set up and the problem is solved.
I have a view based application. Now on the opening view I have some buttons and a picture and a small web view.
The web view has its own .h/.m file the calls a JSON request to fill it. That works great.
My problem is that when the app is closed and reopened the webview is not updating. How to I get that to work?
welcomeMessage.m (connected to webview)
- (void)awakeFromNib{
[NSThread sleepForTimeInterval:1];
NSUserDefaults *gMess =[NSUserDefaults standardUserDefaults];
NSString *myMess=[gMess stringForKey:#"welcomeMessage"];
NSLog(#"WEBVIEW CLASS %#",myMess);
if (myMess == NULL) {
NSString *html = [NSString stringWithFormat:#"<body style ='background-color:#FFFF33' align='center'><p>Welcome</p><p>Check out our Daily Winners</p></body>"];
[welcomeMessage loadHTMLString:html baseURL:[NSURL URLWithString:#"http://www.myapp.com/api/welcome/welcomemessage.php?iappid=37"]];
}
else{
NSString *html = [NSString stringWithFormat:#"<body style ='background-color:#FFFF33' align='center'> %# </body>", myMess];
[welcomeMessage loadHTMLString:html baseURL:[NSURL URLWithString:#"http://www.myapp.com/api/welcome/welcomemessage.php?iappid=37"]];
}
}
Mainviewcontroller json
- (void)viewDidLoad
{
// Create new SBJSON parser object
SBJsonParser *object = [[SBJsonParser alloc] init];
// Prepare URL request to download statuses from Twitter
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.kickintheapp.com/api/welcome/welcomemessage.php?iappid=37"]];
// NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://twitter.com/statuses/public_timeline.json"]];
// Perform request and get JSON back as a NSData object
NSData *response = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
// Get JSON as a NSString from NSData response
NSString *json_string = [[NSString alloc] initWithData:response encoding:NSUTF8StringEncoding];
NSError *jsonParseError;
NSDictionary *status = [object objectWithString:json_string error:&jsonParseError];
if (!status) {
// there's been a parse error; look at jsonParseError
// for example:
NSLog(#"JSON parse error: %#", jsonParseError);
}
NSString *messValue = [status objectForKey:#"message"];
NSUserDefaults *gMess = [NSUserDefaults standardUserDefaults];
[gMess setObject:messValue forKey:#"welcomeMessage"];
}
Check out Apple's documentation on an iOS's lifecycle. You will find all callbacks you need in there:
http://developer.apple.com/library/ios/#documentation/uikit/reference/UIApplicationDelegate_Protocol/Reference/Reference.html
Especially – applicationWillEnterForeground: will be interesting for you.
I got an issue with users rapidly pressing my UIButton causing multiple entries being placed in my database stored online. I have tried all sorts such as hiding the button when it the action is called and some sort of toggle, both have been unsuccessful. Is there anyway to limit the press to just one. the action is linked to the touch up inside reference on the button.
-(IBAction)postData:(id)sender
{
if(loginControl == 0)
{
if(nameIB.text.length && numberIB.text.length > 0)
{
loginControl = 1;
loginButton.hidden = YES;
NSMutableData *data = [NSMutableData data];
NSString *number = numberIB.text;
NSString *name = nameIB.text;
NSString *nameString = [[NSString alloc] initWithFormat:#"name=%#", name];
NSString *numberString = [[NSString alloc] initWithFormat:#"&number=%#", number];
NSString *genderString = [[NSString alloc] initWithFormat:#"&gender=%#", gender];
//NSLog(nameString);
//NSLog(numberString);
[data appendData:[nameString dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[numberString dataUsingEncoding:NSUTF8StringEncoding]];
[data appendData:[genderString dataUsingEncoding:NSUTF8StringEncoding]];
NSURL *url = [NSURL URLWithString:#"http://www.blah.net/blah.php"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request setHTTPMethod:#"POST"];
[request setHTTPBody:data];
NSURLResponse *response;
NSError *err;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
NSLog(#"responseData: %#", responseData);
userData = responseData;
[self startParsingUserId];
logoutButton.hidden = NO;
}
else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Text Fields Empty" message:#"One Or More Textfields Are Empty" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:nil];
[alert show];
[alert release];
loginControl = 0;
}
}
}
You should immediately be setting the enabled or hidden property of the button to disable interaction.
HOWEVER that change will not take effect until the next turn of the runloop, when everything gets re-drawn. As your code stands, your code is hiding the button, doing stuff, and then unhiding the button, all before the button gets a chance to redraw.
What you should do is set the hidden property and then start the computation (preferably on a background thread). Once and only once the computation completes, you should signal the main thread to un-hide the button again.
If you are OK with having this only work on iOS 4.0+, you can easily accomplish this with Grand Central Dispatch:
- (IBAction)doStuff:(id)sender {
[button setEnabled:NO];
dispatch_async(dispatch_get_global_queue(0,0), ^{
// do all your computation/synchronous requesting here
// this will happen on a background thread
dispatch_async(dispatch_get_main_queue(), ^{
[button setEnabled:YES];
});
});
}
In the button's selector, use -performSelectorOnMainThread:withObject:waitUntilDone: to run a chunk of code that disables the button until the method's logic is complete.
We have TextView control with some website url, if we click the textview website url, it is automatically open the safari browser, how to open the this webpage within our application?
I subclass a webview and give it a method inside like this:
-(void)loadWebPageFaceBook{
NSURL *url = [ [ NSURL alloc ] initWithString: #"http://www.facebook.com" ];
NSURLRequest *urlRequest = [[NSURLRequest alloc]initWithURL:url];
tabBar.selectedItem = [tabBar.items objectAtIndex:1];
[self loadRequest:urlRequest];
[url release];
[urlRequest release];
}
the tabBar part is cos I have a tab controller at bottom with a couple hyperlinks, this is like a small sandboxed browser..
obviously you could be feeding the URL as a NSString (#"www.someWebsite.com") argument into the method call, modifying above to something like..
-(void)loadWebPage:(NSString)urlString{
NSURL *url = [ [ NSURL alloc ] initWithString: urlString ];
NSURLRequest *urlRequest = [[NSURLRequest alloc]initWithURL:url];
[self loadRequest:urlRequest];
[url release];
[urlRequest release];
}
and then call that from your view controller, something in the textfield should return...
if ([myTextFieldObject.text length > 0]){
NSString *urlEntry = myTextFieldObject.text;
[myWebViewObject loadWebPage:urlEntry];
the webView object will also need a method to deal with errors, when there is no network/internet connection, and when the URL is bad. hope that helps
Hello
I am sending some values to the server using ASIHTTPRequest. All works fine until yesterday that the requestFinished didnt work. (when the app send the request on the server an activity indicator and a new view added to the main view and when the request finished is removing the views). I added requestFailed to test if is failed and I get this error:
[3438:207] Error Domain=ASIHTTPRequestErrorDomain Code=2 "The request timed out" UserInfo=0x5ad25c0
Its weird because the same code was working fine yesterday. I am sure that they didnt make any changes on the server's side.
this is the code:
- (IBAction)convert:(id)sender{
//Get the email from the textfield
NSString *email1 = email.text;
//Save the last used email to load it on the next app launch
[[NSUserDefaults standardUserDefaults] setValue:email1 forKey:#"email"];
//Get the current URL from webview
NSString *currentURL= webView.request.URL.relativeString;
lbl.text = currentURL;
//Count the length of Label
int strL= [lbl.text length];
//The url that the requests will be send.
NSURL *url = [NSURL URLWithString:#"the website"];
//Indicator and its view are loading on the screen
[ind startAnimating];
[self.view addSubview:indView];
//ASIHTTPRequests
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
NSString *watch = [lbl.text substringWithRange:NSMakeRange(23,strL-23)];
NSString *link = [NSString stringWithFormat:#"http://youtube.com/%#",watch];
[request setShouldRedirect:YES];
[request setPostValue:watch forKey:#"url"];
[request setPostValue:email1 forKey:#"email"];
[request setPostValue:format forKey:#"format"];
[request setPostValue:quality forKey:#"quality"];
[request setDelegate:self];
[request startAsynchronous];
NSLog(#"%# %# %# %#",watch,email1,format,quality);
click=NO;
}
and this is the requestFinished:
- (void)requestFinished:(ASIFormDataRequest *)request{
NSString *responseString = [request responseString];
NSLog(#"%#",responseString);
NSLog(#"%#",lbl.text);
NSLog(#"requested finished");
[ind stopAnimating];
[indView removeFromSuperview];
[setView removeFromSuperview];
}
Did you try to increase the timeout value on the request? By default it is 10 seconds, you can make it larger by doing this right before the startAsynchronous call:
[request setTimeOutSeconds:60];