MBProgressHud not hiding after calling hide from another class - class

I have a ViewController called GetInfoViewController. Basically it takes the users input and then sends the input to a NSObject class, ServerConnection. ServerConnection makes a nsurlconnection request and when it is done I want the MBProgressHUD to hide.
GetInfoViewController
- (IBAction)go:(id)sender{
ServerConnection *serverConnection = [[ServerConnection alloc]init];
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = #"Searching...";
// here the progressHUD shows normally, and the yelpConnectionMethod is successful in retrieving the result.
[serverConnection yelpConnectionMethod];
}
-(void)endProgressHUD{
NSLog(#"end called");
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
ServerConnection.h
I am not going to put all the nsurlconnection code because I don't think it really applies.. but if you want it I can post it
The only line that matters is:
(this is called after all the connections are done.)
GetInfoViewController *getinfoVC = [[GetInfoViewController alloc]init];
[getinfoVC endProgressHUD];
I call the endProgressHUD method of the getInfoViewController successfully, as it logs "end called". however, the progress hud stays spinning and does not hide.
Any input would be helpful.

Try this method when showing the HUD.
- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated;
It is a method of MBProgressHUD - selector should be the yelpConnectionMethod and target the serverConnection, object = nil and animated depends on you :)

Related

MBProgressHUD activity indicator doesn't show in viewDidAppear after segue is performed

I have two UITableViewControllers A and B, and this is what I'm trying to do when I click on a table view cell in A:
Prepare to segue from A to B by setting some of B's variables from A.
Perform segue from A to B.
B appears.
Display a "Loading" activity indicator with [MBProgressHUD][1].
In a background task, retrieve data from a URL.
If an error occurs in the URL request (either no data received or non-200 status code), (a) hide activity indicator, then (b) display UIAlertView with an error message
Else, (a) Reload B's tableView with the retrieved data, then (b) Hide activity indicator
However, this is what's happening, and I don't know how to fix it:
After clicking a cell in A, B slides in from the right with an empty plain UITableView. The MBProgressHUD DOES NOT SHOW.
After a while, the tableView reloads with the retrieved data, with the MBProgressHUD appearing very briefly.
The MBProgressHUD immediately disappears.
There doesn't seem to be an error with the way the background task is performed. My problem is, how do I display the MBProgressHUD activity indicator as soon as my B view controller appears? (And actually, how come it's not showing?) Code is below.
A's prepareForSegue
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
B *b = (B *)[segue destinationViewController];
// Set some of B's variables here...
}
Relevant methods in B
- (void)viewDidAppear:(BOOL)animated {
[self startOver];
}
- (void)startOver {
[self displayLoadingAndDisableTableViewInteractions];
[self retrieveListings];
[self.tableView reloadData];
[self hideLoadingAndEnableTableViewInteractions];
}
- (void)displayLoadingAndDisableTableViewInteractions {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading";
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
self.tableView.userInteractionEnabled = NO;
}
- (void)hideLoadingAndEnableTableViewInteractions {
[MBProgressHUD hideHUDForView:self.view animated:YES];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
self.tableView.userInteractionEnabled = YES;
}
- (void)retrieveListings {
__block NSArray *newSearchResults;
// Perform synchronous URL request in another thread.
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
newSearchResults = [self fetchNewSearchResults];
});
// If nil was returned, there must have been some error--display a UIAlertView.
if (newSearchResults == nil) {
[[[UIAlertView alloc] initWithTitle:#"Oops!" message:#"An unknown error occurred. Try again later?" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil] show];
} else {
// Add the retrieved data to this UITableView's model. Then,
[self.tableView reloadData];
}
}
- (NSArray *)fetchNewSearchResults {
// Assemble NSMutableArray called newSearchResults from NSURLConnection data.
// Return nil if an error or a non-200 response code occurred.
return newSearchResults;
}
I think you have to call [self hideLoadingAndEnableTableViewInteractions]; after newSearchResults = [self fetchNewSearchResults]; You are retrieving data in another thread which means -startOver will continue executing after calling [self retrieveListings]; and will hide the HUD right away. Also because you are updating the display you have to make sure you are doing that on the main thread. See example
dispatch_async(dispatch_get_main_queue(), ^{
//update UI here
});
When B appears, it displays a plain and empty UITableView, but does not display the MBProgressHUD even if the task does begin in the background (and yet, the MBProgressHUD is called to show before that). Hence, my solution is to show the MBProgressHUD in viewDidLoad, which precedes viewWillAppear.
- (void)viewDidLoad {
// ...
[self displayLoadingAndDisableUI];
}
I set up two additional boolean properties to B--one in .h, called shouldStartOverUponAppearing, and one in a class extension in .m, called isLoadingAndDisabledUI. In startOver, I added the following lines:
- (void)startOver {
if (!self.isLoadingAndDisabledUI) {
[self displayLoadingAndDisabledUI];
}
}
The check is done so that startOver doesn't display another MBProgressHUD when it has already been displayed from viewDidLoad. That is because I have a third view controller, called C, that may call on B's startOver, but doesn't need to call viewDidLoad just to display the MBProgressHUD.
Also, this is how I defined viewDidAppear:
- (void)viewDidAppear:(BOOL)animated {
if (self.shouldStartOverUponAppearing) {
[self startOver];
self.shouldStartOverUponAppearing = NO;
}
}
This way, startOver will only be invoked IF B appeared from A. If B appears by pressing "Back" in C, it will do nothing and only display the old data that was there.
I think that this solution is FAR from elegant, but it works. I guess I'll just ask for a better approach in a separate SO question.
I have used a common method for MBProgressHUD.
#import "MBProgressHUD.h" in AppDelegate.h also following methods.
- (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title;
- (void)dismissGlobalHUD;
In AppDelegate.m add following methods.
- (MBProgressHUD *)showGlobalProgressHUDWithTitle:(NSString *)title {
[MBProgressHUD hideAllHUDsForView:self.window animated:YES];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.window animated:YES];
hud.labelText = title;
return hud;
}
- (void)dismissGlobalHUD {
[MBProgressHUD hideHUDForView:self.window animated:YES];
}
How to use?
AppDelegate *appDel = (AppDelegate *)[[UIApplication sharedApplication]delegate];
//Show Indicator
[appDel showGlobalProgressHUDWithTitle:#"Loading..."];
//Hide Indicator
[appDel dismissGlobalHUD];
Hope this helps.

Dismiss MBProgressHUD when delegate method fires

First time working with a HUD and I'm confused.
I setup the HUD like this in my viewDidLoad:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[[[WSXmppUserManager shared] xmppStreamManager] connect];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
The HUD doesn't show. I think the reason is as follows. The xmpp connect method fires off a connection request to the xmpp server and then it's done. So there is no activity to wait for as is.
However, the connection isn't established until the server responds and the following delegate method is fired:
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
I want to wait for this and only then dismiss the HUD, but I'm at a loss as to how to do that. I'm missing something very simple.
You need to move this code
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
After your long running method has finished... that is, if this code is indeed returning immediately
[[WSXmppUserManager shared] xmppStreamManager] connect];
The hud is likely never going to display... as it gets told to display and then told to hide on the same run loop or perhaps one run loop right afterwards...
Why not put it at the end of this method if this indicates that a response has been received and the operation is completed?
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender
HUD =[[MBProgressHUD alloc] initWithWindow:self.view];
[HUD setDelegate:self];
[self.view addSubview:HUD];
[HUD showWhileExecuting:#selector(connectToServer)
onTarget:self
withObject:nil
animated:YES];
In the connectToServer
-(void)connectToServer
{
[[[WSXmppUserManager shared] xmppStreamManager] connect];
}
As soon as the connectToServer method comepletes it task in the background , a delegate of MBProgressHUD called hudWasHidden: is automatically called
-(void)hudWasHidden:(MBProgressHUD *)hud
{
//Further work after the background task completed
}

Activity Indicator is not showing with SVProgressHUD

I am trying to implement the SVProgressHUD progress activity indicator. I copied the class from the [demo].1
My app loads up but the activity indicator doesn't show up. This is my first time trying to use one of these, so any help would be appreciated.
Here is the code:
#import "SVProgressHUD.h"
#implementation QuotesAppDelegate
- (void)startLoading
{
//call this in your app delegate instead of setting window.rootViewController to your main view controller
//you can show a UIActivityIndiocatorView here or something if you like
[SVProgressHUD show];
[self performSelectorInBackground:#selector(loadInBackground) withObject:nil];
}
- (void)loadInBackground
{
//do your loading here
//this is in the background, so don't try to access any UI elements
[self populateFromDatabase];
[SVProgressHUD dismiss];
[self performSelectorOnMainThread:#selector(finishedLoading) withObject:nil waitUntilDone:NO];
}
- (void)finishedLoading
{
//back on the main thread now, it's safe to show your view controller
[window addSubview:[navigationController view]];
[window makeKeyAndVisible];
}
- (void)applicationDidFinishLaunching:(UIApplication *)application {
[self copyDatabaseIfNeeded];
[self startLoading];
}
For anyone else having a similar problem, this can also happen because you have a long loop or a piece of code that takes a long time to execute. If this happens, your progress bar wont be shown until after the loop, which kind of defeats the purpose.
To solve this issue you need to you this:
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg
Basically your code would look something like this:
- (IBAction)submitPost:(id)sender {
//now we show the loading bar and submit the comment
[SVProgressHUD showWithStatus:#"Submitting post" maskType:SVProgressHUDMaskTypeGradient];
SEL aSelector = #selector(submitDataOfPost);
[self performSelectorInBackground:aSelector withObject:sender];
}
This will basically load the progress bar, and in a background thread, the method you want to execute will be called. This makes sure that the UI is updated (shows the progress hud) at the same time that your code is executed.
First of all you were not adding your SVProgressHUD to the view.
If your class inherited from UIViewController then [self.view addSubview:]; or if your class is simple UIView then [self addSubView:];
I do not understand your requirement but as far as i can understand through your code that you are showing [SVProgressHUD show]; in your startLoading method and then you are calling loadInBackground method in that method where you are hiding your hud using [SVProgressHUD dismiss];
I will suggest you to trace it by using breakpoint and figure it out.
I had the same problem. When I changed a version of SVProgressHUD to the later one the problem disappeared. My current version supports ARC.
=>
(IBAction)fetchData:(id)sender
{
[SVProgressHUD showWithStatus:#"Loading..." maskType:SVProgressHUDMaskTypeGradient];
[self performSelectorOnMainThread:#selector(getDataFromSomeWhere) withObject:nil waitUntilDone:NO];
}
=>
(void)getDataFromSomeWhere
{
//Do your data populating here. and dismiss the ProgressHud.
[SVProgressHUD dismiss];
}

Using MBProgressHUD activity indictor

so I have a table view and whenever user press a row, another class view shows up. So I wanted to have a loading indicator in between the transition. I am using MBProgressHUD, but it showed nothing when I pressed the row. And what should I put inside the #selector()?
[loading showWhileExecuting:#selector() onTarget:self withObject:[NSNumber numberWithInt:i] animated:YES];
Here is my code.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
loading = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:loading];
loading.delegate = self;
loading.labelText = #"Loading Events, Please Wait..";
[loading showWhileExecuting:#selector(//what should I put) onTarget:self withObject:nil animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ([[self.citiesArray objectAtIndex:indexPath.row] isEqual:#"NEW YORK"])
{
self.newYorkViewController = [[NewYorkViewController alloc] initWithNibName:#"NewYorkViewController" bundle:nil];
Twangoo_AppAppDelegate *delegate = (Twangoo_AppAppDelegate*)[[UIApplication sharedApplication] delegate];
[delegate.citiesNavController pushViewController:self.newYorkViewController animated:YES];
}
}
You need to implement a waiting function so that when the control returns from that method the HUD will hide/disappear from the screen. So its basically what you do when the HUD is being displayed on the screen, it may be some processing or wait for response for a http request etc. It can also be a timer.
- (void)waitForResponse
{
while (/*Some condition is not met*/)
{
}
}
Also you need to implement the
- (void) hudWasHidden
{
[HUD removeFromSuperview];
[HUD release];
}
You might have a look to the Cocoa documentation chapter on selectors
Selector can be simply see as a pointer to a function.
Then, I guess you are trying to display a progress hud while a particular process is running .. this particular process logically should be isolated in a dedicated method (let's call it doTheJob ).
So start by creating a dedicated method named whatever ( here doTheJob )
- (void) doTheJob;
That being said the MBProgressHUD allows you to simply specify the working method that should be handled by the progress information using the showWhileExecuting method. And the selector is here to defined the target worker method.
[loading showWhileExecuting:#selector(doTheJob) onTarget:self withObject:nil animated:YES];
The target would be the object reference that defines the selector. To remain simple, if you define the method doTheJob in the current class use self as target.
and the withObject, is any parameter that you want / need to provide to the selector method. Beware that if you need to provide parameter to the target method, you need to extend the selector definition with an trailing colon as #selector(doTheJob:)
Hope this helps.

Iphone - UIView not displayed

I have a strange problem with a UIView :
I want to show an Activity Indicator View that I created with Interface Builder to indicate long running activity.
In the viewDidLoad function of my principal viewController I init the ActivityIndicator View like this :
- (void)viewDidLoad {
[super viewDidLoad];
appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
load = [[ActivityIndicatorViewController alloc] init];
...
When I push a button it call this IBAction :
- (IBAction)LaunchButtonPressed{
// Show the Activity indicator view.
[self.view addSubview:load.view];
// eavy work
[self StartWorking];
// Hide the loading view.
[load.view removeFromSuperview];
}
In the StartWorking function, I ask a request to an internet serveur and parse the XML file that it return me.
The problem is that if I Call my StartWorking function, the application do not start by showing the Activity Indicator view but with the StartWorking function.
Whereas if I remove the call to StartWorking function, the view is shown.
Is someone able to explain me why? :s
Have you tried to call the StartWorking method on a different thread?
Maybe its heavy process prevents other instructions to take place.
Look at the NSThread class, especially the detachNewThreadSelector:toTarget:withObject: method.
EDIT: About the pool problem, you need to create a pool in your StartWorking method, if it's called on a different thread:
- ( void )StartWorking
{
NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
/* Code here... */
[ pool release ];
}
Replace :
[self.view addSubview:load.view];
With :
[self performSelector:#selector(addLoadingSubview) afterDelay:0.1f];
And create the method :
-(void)addLoadingSubview{[self.view addSubview:load.view];}
Ok, I found a solution based on santoni answer :
- (IBAction)LaunchButtonPressed{
// Show the Activity indicator view.
[self performSelector:#selector(ShowActivityIndicatorView) withObject:nil afterDelay:0];
// eavy work
[self performSelector:#selector(StartWorking) withObject:nil afterDelay:2];
// Hide the loading view.
[load.view removeFromSuperview];
}
The Activity Indicator view is dislayed before the call to the eavy function.
Thank's for answering.