MBProgressHUD Hiding indicator after GCD - ios

The user is able to click a button to download a set of maps, upon completion of that task, I would like to hide the progress indicator. I have tried several variants of the below code but have not achieved what I am searching for. Any guidance would be appreciated.
- (IBAction)SavePhotoOnClick:(id)sender{
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSURL *url = [NSURL URLWithString:urlstr1];
UIImage *image = [UIImage imageWithData:[NSData dataWithContentsOfURL:url]];
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil); dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
// Set determinate mode
HUD.mode = MBProgressHUDModeDeterminate;
HUD.dimBackground = YES;
HUD.color = [UIColor colorWithRed:0.23 green:0.50 blue:0.82 alpha:0.90];
HUD.delegate = self;
HUD.labelText = #"Downloading Maps";
// myProgressTask uses the HUD instance to update progress
[HUD showWhileExecuting:#selector(myProgressTask) onTarget:self withObject:nil animated:YES];
}
- (void)myProgressTask {
// This just increases the progress indicator in a loop
float progress = 0.0f;
while (progress < 1.0f) {
progress += 0.01f;
HUD.progress = progress;
usleep(40000);
}
}
Removing the progresstask method and the showWhileExecuting code removes the progress indicator. It seems that usleep overrides everything and does not allow for the progress indicator to be hidden after the download completes and instead removes it after the 4 sec.

Your problem is that myProgressTask doesn't perform any actual work: your download actually happens in your dispatch_async call. Use a call like [hud showHUDAddedTo: self.navigationController.view animated:YES]; before you start your network request, so it will become hidden when your download completes. This will only show the spinning indicator, not a progress circle.
If you want the progress circle, you will need to use NSURLConnection and its associated delegate methods to track the progress of the download. Specifically, look at the connection:didReceiveResponse: method—using the expectedContentLength to calculate the percentage, using values you get from the connection:didReceiveData: method to incrementally update this progress.
Check out this question for an example of how to do this.

Related

UIActivityIndicatorView not staring

I am trying to set a 'UIActivityIndicatorView' as an accessoryView in one of my 'UITableViewCell', in a such way as, the ActivityIndicator start animating when the user touch this cell. I add the following code at 'didSelectRowAtIndexPath' method:
UITableViewCell * cell = [tableView cellForRowAtIndexPath:indexPath];
UIActivityIndicatorView * activity = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
NSThread * newThread1 = [[NSThread alloc]initWithTarget:self selector:#selector(carregarNoticias) object:nil];
switch (indexPath.row) {
case 0:
cell.accessoryView = activity;
[activity startAnimating];
sleep(0.01);
[newThread1 start];
while (![newThread1 isFinished]) {
//waiting for thread
}
[activity stopAnimating];
[self.navigationController pushViewController:noticias animated:YES];
break;
I was expecting 'UIActivityIndicatorView' animating while the newthread1 is running. However, the animating only start when the newThread1 finish (and therefore I don't want the animation anymore). I added 0.01 seconds as sleep time to give time to the animation start. Nevertheless, this also did't solve.
Does anyone know what my error is? I appreciate any help!
I dont really know what you are trying to do adding delay like that.. try this line below, might be the one your look for.. and another thing.. please dont block the main thread like sleep(0.01); that, users wont like it..
cell.accessoryView = activity;
[activity startAnimating];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
// the system will wait until this
dispatch_async(dispatch_get_main_queue(), ^(void){
[newThread1 start];
});
// is finished, maybe you dont need the `[NSThread sleepForTimeInterval:3];`
// pick the one that suits your implementation..
[NSThread sleepForTimeInterval:3];
dispatch_async(dispatch_get_main_queue(), ^(void){
[activity stopAnimating];
[self.navigationController pushViewController:noticias animated:YES];
});
});
[NSThread sleepForTimeInterval:3] sleep the thread like the one you have sleep(0.01);
But using it inside
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){ });
makes it async. and wont block the main thread..
while the dispatch_async(dispatch_get_main_queue(), ^(void){ fires your code at the main thread..
Happy coding, cheers! :)

MBProgressHUD fades / lightens when triggered by this button

What is going on here?
http://screencast.com/t/WACeaQP00iqb
Here's the code for that button (the three method calls spawn 3 (yes 3; i didn't write the server :D) asynchronous data tasks that take some time to finish):
- (IBAction)didTouchClockButton:(id)sender {
[self.dr isUserClockedIn];
hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.dimBackground = NO;
// hail mary
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate distantPast]];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
if (![self.dr isNetworkDead])
if ([self.dr isUserClockedIn]) { // add clock in / clock out function to this button
hud.labelText = #"Clocking Out..";
[self.dr clockOut];
} else {
hud.labelText = #"Clocking In..";
[self.dr clockIn];
}
});
}
I put the hud dismissal inside a KVO callback, which isn't working yet but first things first.
You don't want to do anything related to the UI on a background thread. Dispatch the MBProgressHUD code on the main thread and see if that solves the issue. Try:
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
if (![self.dr isNetworkDead])
if ([self.dr isUserClockedIn]) { // add clock in / clock out function to this button
dispatch_async(dispatch_get_main_queue(), ^{
hud.labelText = #"Clocking Out..";
});
[self.dr clockOut];
} else {
dispatch_async(dispatch_get_main_queue(), ^{
hud.labelText = #"Clocking Out..";
});
[self.dr clockIn];
}
});
I ended up dismissing the HUD the line before I call the method that updates the other view elements. Right now they're both happening near instantaneously so it's fine, although if the view was still updating after the HUD went down that would be lame. So the solution is to not mess with the view while your HUD is active. A messy solution, so if anyone has a better one I'm all ears.

Activity Indicator is not showed when loading large amount of data from sqlite database

In my app, i'm getting the data from database. The data that I get from database is very large like 20000 records. So, I want to show an activity indicator to indicate that data is loading. But activity indicator is not showed when I run the app.
I'm sending code for this. Please help me out.
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.frame = CGRectMake(100,100, 20, 20);
[self.view addSubview:activityIndicator];
[activityIndicator startAnimating];
// Code to get data from database
[activityIndicator stopAnimating];
First, you don't need to set the frame for the activiyIndicator. You can just set the centre...
activityIndicator.center = self.view.center; (or soemthing like this).
Second, how are you retrieving the data? Are you doing it all on the main thread? Or are you doing it asynchronously?
::EDIT::
Ah it appears you are doing this...
[actInd startAnimating];
[self getData]; //this is asynchronous
[actInd stopAnimating];
if this is the case then the stopAnimating will get called almost immediately.
What you need is something like this...
[actInd startAnimating];
[self getData]; //this is asynchronous
Then in getData...
- (void)getData
{
//get all your data and process it....
[actInd stopAnimating];
}
This way the stop animating will only get called after doing all the data work.
If that doesn't work try removing the stopAnimating altogether to check it is actually animating in the first place.
#Fogmeister is wright about setting the frame of activityIndicator.
and for showing the activityIndicator just create one function of the code shown in your question and call it using the performSelector:withObject:afterDelay: with delay of 0.001 and the activityIndicator will shown
Happy Coding :)
Do this:
activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
activityIndicator.center = self.view.center;
[self.view addSubview:activityIndicator];
[NSThread detachNewThreadSelector:#selector(startActivityIndicator) toTarget:self withObject:nil];
Add this method:
-(void)startActivityIndicator
{
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
[activityIndicator startAnimating];
[pool release];
}
Do changes:
- (void)getData
{
//get all your data from database.
[activityIndicator stopAnimating];
}

ASINetworkQueue and MBProgressHUD

I use a ASINetWorkQueue in a ViewController. So, during the queue is performing, i want to show a MBProgressHUD.
- (void) addItemsToEndOfTableView{
NSLog(#"add items");
[[self networkQueue] cancelAllOperations];
// Création d'une nouvelle file (queue) de requetes
[self setNetworkQueue:[ASINetworkQueue queue]];
[[self networkQueue] setDelegate:self];
[[self networkQueue] setRequestDidFinishSelector:#selector(requestFinished:)];
[[self networkQueue] setRequestDidFailSelector:#selector(requestFailed:)];
[[self networkQueue] setQueueDidFinishSelector:#selector(queueFinished:)];
...add requests
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.dimBackground = YES;
HUD.delegate = self;
[HUD showWhileExecuting:#selector(stopHub) onTarget:self withObject:nil animated:YES];
}
[[self networkQueue] go];
so, when queueFinished is call, i want to stop the hud:
- (void)queueFinished:(ASINetworkQueue *)queue
{
[self stophud];
}
-(void)stophud
{
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
but actually, the progress Hud disappear quickly, whereas activity indicator in the top bar of iphone is running while data being collect.
So, what's wrong ?
From MBprogressHUD API
/**
* Shows the HUD while a background task is executing in a new thread, then hides the HUD.
*
* This method also takes care of autorelease pools so your method does not have to be concerned with setting up a
* pool.
*
* #param method The method to be executed while the HUD is shown. This method will be executed in a new thread.
* #param target The object that the target method belongs to.
* #param object An optional object to be passed to the method.
* #param animated If set to YES the HUD will (dis)appear using the current animationType. If set to NO the HUD will not use
* animations while (dis)appearing.
*/
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;
Since you are using this method, your stophud is executed in a new thread. This could cause the strange problem you have (I suppose).
Insetad of using it, try to use
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
// set the properties here...
to show it after starting the queue and
[MBProgressHUD hideHUDForView:self.view animated:YES];
to hide it when the queue has finished.
Hope it helps.

MBProgressHUD change label.text in connectionDidFinishLoading

I'm using MDProgressHUD to manage the progress of my downloads which uses NSConnection.
Everything is working great. I'm having an issue when I try to change the HUD labelText from saying Downloading to Finishing Up when connectionDidFinishLoading is called.
In my connectionDidFinishLoading method I'm changing the labelText and changing the icon to a checkmark , like in the example app.
HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"37x-Checkmark.png"]];
HUD.mode = MBProgressHUDModeCustomView;
HUD.labelText = #"Finishing Up";
NSLog(#"show change now!!!");
Right after that code I unzip the download and do some db manipulation.
But for some reason it doesn't change until the end of the function is reached where
[HUD hide:YES afterDelay:4]; is called.
I would like it to change before it starts to unzip my contents because the download is full and it makes the app look like its hanging or frozen.
I'm using zipArchive to do the extraction if it matters.
Any advice would be appreciated.
This worked for me
dispatch_async(dispatch_get_main_queue(), ^{
progressView.titleLabelText = #"Downloading ...";
});
For anyone else coming back to this, note that setting:
HUD.hidden = YES;
is not sufficient for the delegate method to be called. You must actually call:
[HUD hide:YES];
Did you try with:
[HUD setNeedsLayout];
[HUD setNeedsDisplay];
MBProgressHUD uses KVO for detecting changes to properties like labels, progress, etc. As soon as you change it observeValueForKeyPath() should run and update the UI. Is there any chance that your code is really intensive and is blocking the UI? I've seen that happen before..
You should just add this method to the header file of MBProgressHud:
+ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view withText:(NSString *)text;
And implement it in the .m file as follows:
+ (MB_INSTANCETYPE)showHUDAddedTo:(UIView *)view withText:(NSString *)text
{
MBProgressHUD *hud = [[self alloc] initWithView:view];
hud.labelText = text;
[view addSubview:hud];
[hud show:YES];
return MB_AUTORELEASE(hud);
}
and call it wherever you want like:
[MBProgressHUD showHUDAddedTo:self.view withText:#"Loading..."];

Resources