UIActivityIndicator not animating - iphone

I've been beating my head against a wall with this problem, and need some help.
I am trying to display a UIActivityIndicator while I load data in the background. I'm not sure if this is relevant or not, but I am loading a tableview. The indicator appears, but doesn't spin...unless I touch the screen, or something else happens while loading-like if I receive a text message. Here is the code:
UIActivityIndicatorView *av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
av.frame=CGRectMake(145, 160, 25, 25);
av.tag = 1;
[self.mTableView addSubview:av];
[av startAnimating];
[self performSelectorInBackground:#selector(load) withObject:nil];
I've also tried this:
UIActivityIndicatorView *av = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
av.frame=CGRectMake(145, 160, 25, 25);
av.tag = 1;
[self.view addSubview:av];
[av startAnimating];
[self performSelectorInBackground:#selector(load) withObject:nil];
and have tried it with commenting out the last line-So not having the background thread run. I have tried both versions of the code in my viewDidLoad and viewDidAppear methods.
Any ideas?
Edit Here is my load method
- (void)load {
NSString *post = [NSString stringWithFormat:#"id[]=%#", [ids objectAtIndex:0]];
for(int i = 1; i < ids.count; i++){
post = [NSString stringWithFormat:#"%#&id[]=%#", post, [ids objectAtIndex:i]];
}
NSURL *url=[NSURL URLWithString:#"http://myurl/instructions"];
NSData *postData = [post dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:url];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
/* when we user https, we need to allow any HTTPS cerificates, so add the one line code,to tell teh NSURLRequest to accept any https certificate, i'm not sure about the security aspects
*/
//[NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[url host]];
NSError *error;
NSURLResponse *response;
NSData *urlData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSString *data=[[NSString alloc]initWithData:urlData encoding:NSUTF8StringEncoding];
[self parseData:data];
[spinner stopAnimating];
}

You didn't include enough code to see where you're stopping the UIActivityIndicator animation and what's going on while you're displaying it, but the problem almost certainly is one of the following:
1) You're expecting your code to wait for some asynchronous method, which is causing your activity indicator to get shut off prematurely,
or
2) Both the "background" task and the UIActivityIndicator are running on the same thread, and your "background" task is monopolizing the thread to the point where the activity indicator doesn't get enough time to animate.
This post provides an example of how to push the UIActivityIndicator into its own thread:
Activity Indicator doesn't spin

The UI needs time to load, and can't start the animation immediately. By starting it with an NSTimer in the viewDidLoad method, the problem was solved.
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(spin) userInfo:nil repeats:NO];
[[NSRunLoop mainRunLoop] addTimer:self.timer forMode:UITrackingRunLoopMode];
and the scan method:
spinner = [[[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite] autorelease];
spinner.center = self.view.center;
spinner.hidesWhenStopped = YES;
[spinner startAnimating];
[self.view addSubview:spinner];
if ([spinner isAnimating]) NSLog(#"animating");
else NSLog(#"not animating");
[self performSelectorInBackground:#selector(load) withObject:nil];
Hopefully this helps.

It's not clear where you're adding the activity indicator but, if it's not on the main thread, then UI calls to it may not work. I typically set up a separate routine to start & stop my activity monitors, so I can performSelectorInMainThread: them.

Not related to the activity indicator, but your load method rewrites the post variable in a loop. I think you intend to concatenate:
post = [post stringByAppendingFormat: ...]
Otherwise your web service will see only the last param.
Also, once you get your spinner animating, the next problem you'll have is that it won't stop because stopAnimating is being called off the main thread.

Related

iPhone/Objective-C: How to modify UILabel from timer

I have the following timer:
uploadGPS_timer=[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:#selector(uploadGPS_tick:) userInfo:nil repeats:YES];
[self uploadGPS_tick:nil];
And here is the callback: uploadGPS_tick():
-(void)uploadGPS_tick:(NSTimer*)timer{
if(!lat || !lng){
//do nothing
}else{
NSString *urlStr=[[NSString alloc] initWithFormat:#"http://www.example.com/ajax/updateCoords.php"];
NSURL *url=[NSURL URLWithString:urlStr];
__block ASIFormDataRequest *request=[[ASIFormDataRequest alloc ]initWithURL:url];
[request setPostValue:lat forKey:#"lat"];
[request setPostValue:lng forKey:#"lng"];
NSLog(#"EOH: %#",lat);
[request setDelegate:self];
[request setCompletionBlock:^{
NSString *response=[request responseString];
NSLog(#"%#",response);
if([response isEqualToString:#"LO"]){
[self.navigationItem setBackBarButtonItem:nil];
DriverLogin *x= [[DriverLogin alloc] initWithNibName:nil bundle:nil];
[[self navigationController]pushViewController:x animated:NO];
}
SBJsonParser *parser=[[SBJsonParser alloc]init];
NSMutableDictionary *obj=[[NSMutableDictionary alloc]init ];
obj=[[parser objectWithString:[request responseString] error:nil]retain];
credits.text=[[obj objectForKey:#"credits"] stringValue]; //this won't show...
creditsUsed.text=[[obj objectForKey:#"creditsUsed"] stringValue]; //this won't show...
NSInteger timeLeftSecs=[[obj objectForKey:#"creditTimeLeft"] intValue];
NSInteger timeLeftMins=(int)(timeLeftSecs/60);
creditTimeLeft.text=[[NSString alloc]initWithFormat:#"%d",timeLeftMins]; //this won't show...
NSLog(#"xxx:%#",obj);
}];
[request setFailedBlock:^{
NSError *error =[request error];
NSLog(#"%#",error);
//do nothing
}];
[request startAsynchronous];
}
}
As you can see, every five seconds, a JSON object is sent from the server. This JSON object is then parsed and three UILabels are set based on this JSON data.
The trouble I'm having is that the UILabels aren't getting their text set! Even though I can clearly see NSLog(#"xxx:%#",obj); in the debugger. The UILabels are connected properly in the .xib.
Any help greatly appreciated.
You should do it from main thread. Replace the label text assignment with following code:
dispatch_async(dispatch_get_main_queue(), ^{
creditTimeLeft.text=[NSString stringWithFormat:#"%d",timeLeftMins];
});

How to periodically poll data from server on a background thread?

I have a chat like app, and to receive periodic chats, I want to poll server at regular intervals , say 5 mins..and check if there are any new messages.
As a beginner, I have tried simple NSURLConnection and send and receive data, but stuck while polling.
What is a correct way of polling? Should I use NSTimer to poll regularly?
Is there any sample code that can help me?
I have a timer running on main thread which invokes a background thread every 5 seconds and sends request to server. Now what happening is, the delegates for NSURLConnection are not getting called. What can be done?
Please help.
Thanks
You can launch an NSTimer on a background thread and make calls to the main thread periodically, this is how I do it:
[self performSelectorOnMainThread:#selector(LaunchTimer) withObject:nil waitUntilDone:NO];
Just make a "LaunchTimer" function that calls an update function at a certain interval, which makes a call to NSURLConnection, and when you receive the new messages, update the chat window by making a call to the main thread like so:
[self performSelectorOnMainThread:#selector(update:) withObject:newMessages waitUntilDone:NO];
Continuing from answer of Patrick :- If you can try NSURLConnection without delegate,
-(void)update:(id)objmessage
{
NSString *post =[NSString stringWithFormat:#"timestamp=%#&user_id=%#",DatetimeString,UID];
NSData *postData = [post dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
[request setURL:[NSURL URLWithString:#"Your server URL"]];
[request setHTTPMethod:#"POST"];
[request setValue:postLength forHTTPHeaderField:#"Content-Length"];
[request setValue:#"application/x-www-form-urlencoded charset=utf-8" forHTTPHeaderField:#"Content-Type"];
[request setHTTPBody:postData];
NSError *error;
NSURLResponse *response;
NSData *returnData=[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
returnString=[[NSString alloc]initWithData:returnData encoding:NSUTF8StringEncoding];
if(!returnString)
{
UIAlertView *erroralert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"There is an error getting response" delegate:self cancelButtonTitle:nil otherButtonTitles:#"OK", nil];
[erroralert show];
[erroralert release];
}
else
{
NSLog(#"%#",returnString);
}
SBJSON *json = [[SBJSON new] autorelease];
NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithDictionary:[json objectWithString:returnString error:nil]];
//NSLog(#"%#",dic);
}
Solved this with following method:
[self performSelectorOnMainThread:#selector(performPollinginBackground) withObject:nil waitUntilDone:NO];
-(void)performPollinginBackground{
//call server task here
}

iPhone:Connection delegates not called

In my app,everything works fine when run for first time.When I navigate through other views and went back to the same view,I used viewwillappear to call functions.
On this run,the connection request line gets executed but the connection delegates like didReceiveResponse,didreceivedata,didfinishloading,didFailWithError are not being called.
-(void)viewWillAppear:(BOOL)animated{
[self recentOrderselection];
[self recentOrderStatus];
[super viewWillAppear:animated];
[self.tableview reloadData];
}
-(void)recentOrderStatus{
..................
NSData *requestData = [NSData dataWithBytes:[json1 UTF8String] length:[json1 length]];
[request setValue:#"text/plain" forHTTPHeaderField:#"content-type"];
[request setHTTPBody: requestData];
connection = [[NSURLConnection alloc]initWithRequest:request delegate:self];
NSLog(#"URL>>>%#",url);//Got correct URL in log
}
You should always put [super viewWillAppear:animated]; at first line
And check whether the viewWillAppear function is called when you came back to same view

NSURLConnection Bug when I run It in background.... how can I fix?

I've this code but I can't understand why I must run this on the main thread.
If I run this in the background It doesn't do the post request. Is it a bug?
How can I solve this problem?
- (void)setRead:(MWFeedItem *)entry
{ [self getToken:YES];
NSString *url=[NSString stringWithFormat:#"https://www.google.com/reader/api/0/edit-tag?a=user/-/state/com.google/read&i=%#&T=%#", entry.identifier, token];
[self postRequestWithURLState:url];
}
- (void)postRequestWithURLState:(NSString *)url
{
NSString *bodyRequest = nil;
NSURL *requestURL = [NSURL URLWithString:url];
NSMutableURLRequest *theRequest = [[NSMutableURLRequest alloc] init];
//NSLog(#"-------------- bodyRequest: %#", bodyRequest);
[theRequest setURL:requestURL];
[theRequest setTimeoutInterval:0.5];
[theRequest setHTTPMethod:#"POST"];
[theRequest setHTTPBody:[bodyRequest dataUsingEncoding:NSASCIIStringEncoding]];
[self.oauthAuthentication authorizeRequest:theRequest];
[NSURLConnection connectionWithRequest:theRequest delegate:self];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
this is my call:
-(void)segnaLettura:(MWFeedItem *)item{
[reader setRead:item];
}
- (void) segnaread:(MWFeedItem *)item{
[self performSelectorOnMainThread:#selector(segnaLettura:) withObject:item waitUntilDone:NO];
}
In order for an asynchronous NSURLConnection to function, it requires the thread's runloop to be processed. While new threads automatically get a runloop, it is up to you to run it.
You can learn how to do this in the Threading Programming Guide, and I can explain it more, but most of the time this isn't what you want. Most of the time in iOS, background threads should be managed with NSOperation or GCD. Generally, if you're manually spawning a thread on iOS 4+, you're doing it wrong. There are exceptions, but not often.
The first question here should be "why do I even have a background thread for this?"
If you really need a background thread, then the way you're doing segnaread is probably fine.

UIButton limiting number of presses

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.