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..."];
Related
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.
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've read and read on SO about this, and I just can't seem to find anything that matches my situation.
I've got MBProgressHUD loading when the view appears, as my app immediately goes to grab some webservice data. My problem is the back button on my navigationcontroller is unresponsive while the HUD is displayed (and therefore while the app gets its data). I want the user to be able to tap to dismiss (or to be able to hit the back button in the worst case) to get the heck out, if it's an endless wait. Here's my code that runs as soon as the view appears:
#ifdef __BLOCKS__
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.navigationController.view animated:YES];
hud.labelText = #"Loading";
hud.dimBackground = NO;
hud.userInteractionEnabled = YES;
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do a task in the background
NSString *strURL = #"http://WEBSERVICE_URL_HERE";
//All the usual stuff to get the data from the service in here
NSDictionary* responseDict = [json objectForKey:#"data"]; // Get the dictionary
NSArray* resultsArray = [responseDict objectForKey:#"key"];
// Hide the HUD in the main tread
dispatch_async(dispatch_get_main_queue(), ^{
for (NSDictionary* internalDict in resultsArray)
{
for (NSString *key in [internalDict allKeys])
{//Parse everything and display the results
}
}
[MBProgressHUD hideHUDForView:self.navigationController.view animated:YES];
});
});
#endif
Leaving out all the gibberish about parsing the JSON. This all works fine, and the HUD dismisses after the data shows up and gets displayed. How in the world can I enable a way to stop all this on a tap and get back to the (blank) interface? GestureRecognizer? Would I set that up in the MBProgressHUD class? So frustrated...
Kindest thanks for any help. My apologies for the long post. And for my ugly code...
No need to extend MBProgressHUD. Simply add an UITapGestureRecognizer to it.
ViewDidLoad
:
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:NO];
HUD.mode = MBProgressHUDModeAnnularDeterminate;
UITapGestureRecognizer *HUDSingleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:#selector(singleTap:)];
[HUD addGestureRecognizer:HUDSingleTap];
And then:
-(void)singleTap:(UITapGestureRecognizer*)sender
{
//do what you need.
}
The MBProgressHUD is just a view with a custom drawing to indicate the current progress, which means it is not responsible for any of your app's logic. If you have a long running operation which needs to be canceled at some point, you have to implement this yourself.
The most elegant solution is to extend the MBProgressHUD. You can either draw a custom area which plays the role of a button, add a button programmatically or just wait for a tap event on the whole view. Then you can call a delegate method whenever that button or the view is tapped.
It can look like this:
// MBProgressHUD.h
#protocol MBProgressHUDDelegate <NSObject>
- (void)hudViewWasTapped; // or any other name
#end
// MBProgressHUD.m
// Either this, or some selector you set up for a gesture recognizer
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
if ([self.delegate respondsToSelector:#selector(hudViewWasTapped)]) {
[self.delegate performSelector:#selector(hudViewWasTapped)];
}
}
you have to set your view controller as the delegate for theMBProgressHUD and act accordingly.
Let me know if you need more clarification on this :)
To have extra information:
You could create contentView in your view
And simply show the hud in your contentView (not in your self.view or self.navigationController.view)
in this way your navigationBar's view will not be responsible for your hudView. So, you can go back from your navigationController's view to previous page.
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];
}
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.