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.
Related
I am developing an app where the user will get to confirm some action via UIAlertView, if he confirms, I call a method that handles the operation, then I prepare to pop the view I am in to go back to another view after the method has been called.
I want to show UIActivityIndicatorView if the user presses confirm for as long as it takes to execute the method and go to that other view. I used startAnimating and stopAnimating in the proper location, but i never get to see the UI UIActivityIndicatorView shown, not for a sec.
I guess its related to some UI issues due to UIAlertView, not sure if I am correct though. I just need a clue on how to use UIActivityIndicatorView properly for a method execution time.
My code:
- (void)viewDidLoad
{
[super viewDidLoad];
self.activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
self.activityIndicator.alpha = 1.0;
self.activityIndicator.hidesWhenStopped = YES;
self.activityIndicator.center = self.view.center;
[self.view addSubview:self.activityIndicator];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if(buttonIndex == 1) {
[self.activityIndicator startAnimating];
ContactsTableViewController *contactTableView = [self getContactsTVC];
[contactTableView applyActionOnCells];
// doing some setup before poping off to the root view controller of my nav controller
[self.activityIndicator stopAnimating];
// then go to rootViewController
[self.navigationController popToRootViewControllerAnimated:YES];
}
}
I'm not 100% certain, but try to comment out the stopAnimating call and see if it shows up.
If that helps, applyActionOnCells probably blocks your main thread (where all UI stuff also happens) and the indicator never has a chance to show up before you hide it again.
In that case, try do the applyActionOnCells call in the background:
if(buttonIndex == 1) {
[self.activityIndicator startAnimating];
__block typeof(self) bself = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
ContactsTableViewController *contactTableView = [bself getContactsTVC];
[contactTableView applyActionOnCells];
dispatch_async(dispatch_get_main_queue(), ^{
[bself.activityIndicator stopAnimating];
// then go to rootViewController
[bself.navigationController popToRootViewControllerAnimated:YES];
});
});
}
Edit: see also an earlier question.
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.
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.
I am using a MBProgressHUD and I need to show it on window as I want to add many full screen views. I set HUD with a text and when i change the text later, its not changing the text. HUD is a retained property of a singleton class. When i show it on view it allow me to do so. But i need to do it with window.
_HUD = [[MBProgressHUD alloc] initWithWindow:[UIApplication sharedApplication].keyWindow];
[[UIApplication sharedApplication].keyWindow addSubview:_HUD];
_HUD.delegate=self;
_HUD.labelText =HUD_TEXT_LOADING;
_HUD.detailsLabelText =HUD_TEXT_PLEASE_WAIT;
[_HUD show:YES];
This is how i am changing the text
-(void) changeHUDText:(NSString*)text andDetailText:(NSString*) detailText
{
if (_HUD) {
if (text)
_HUD.labelText=text;
if (detailText)
_HUD.detailsLabelText=detailText;
}
}
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..."];
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..."];