I'm using MBProgressHUD in my app to make it more clear to the user when the app is waiting for the network.
My app is littered with calls to MBProgressHUD.
Example:
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
[User findBy:#{#"email": email} success:^(NSDictionary *user) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
So every network call is wrapped in an MBprogress show and then hide. There's gotta be a cleaner way.
I'd like to do this on a global level so that when there's network activity then the HUD comes on, when it stops then the HUD turns off.
How can I make the HUD mimic what the iPhone network activity indicator is doing?
I didn't see any API for this.
Note: I'm using AFNetworking 2.0
It should be simple.
You need to locate the "Top" most method that all or most of the requests use (Probably in the your implementation of AFnetworking request.
Use the app window to present the hud so you can present it from you AFNetworking class. Simply add this 2 methods to your class -
+ (MBProgressHUD *)showNetworkHUDWithTitle:(NSString *)title {
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
[MBProgressHUD hideAllHUDsForView:window animated:YES];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
hud.labelText = title;
return hud;
}
+ (void)dismissNetworkHUD {
UIWindow *window = [[[UIApplication sharedApplication] windows] lastObject];
[MBProgressHUD hideHUDForView:window animated:YES];
}
you can also check one of MBProgressHUD "show while" methods -
-(id)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block;
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(MBProgressHUDCompletionBlock)completion;
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue;
- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue
completionBlock:(MBProgressHUDCompletionBlock)completion;
this way the hud will disappear at the end of the execution.
Related
My app is a tweak app running in SpringBoard, in which I want to use MBProgressHUD, but it need to specify a view to add to, so how can I get the font most view of the whole iOS?
I should do like that:
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:fronMostView animated:YES];
hud.mode = MBProgressHUDModeText;
hud.labelText = [Localization localize:#"Finished!"];
[hud hide:YES afterDelay:1];
The best place to add this view is AppDelegate. Add view in to appDelegate window will show to top front for whole application. Also it remains on top until you hide or remove this view from appDelegate window.
Create a global object of MBProgressHUD in AppDelegate class and create show/hide methods.
Call this methods respectively for your convenience use.
For Example:
#property (nonatomic, strong) MBProgressHUD *hud;
- (void) showProgress:(NSString *)message {
// Create a loading view
if (!self.hud) {
_hud = [MBProgressHUD showHUDAddedTo:fronMostView animated:YES];
self.hud.mode = MBProgressHUDModeText;
// Add Progress to window.
[self.window addSubview:self.hud];
}
self.hud.hidden = NO;
self.hud.labelText = [Localization localize:message];
}
- (void) hideLoadingMessage {
self.hud.labelText = [Localization localize:#"Finished!"];
[self.hud hide:YES afterDelay:1];
}
try
UIWindow* window = [[UIApplication sharedApplication] keyWindow];
or
UIWindow *window = [UIApplication sharedApplication].windows.lastObject;
Then
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:window animated:YES];
Here is a topic about this, maybe it'll be useful.
What I need
I'd like to show an UIAlertView "Loading..." when I tap on a tab in my tabBar.
Reason why I want to do that
This is because that particular tab contains a UIWebView and is very slow, so when I tap on that tab it seems like my app is doing absolutely nothing for a few seconds. It is very ugly, so I'd like to show something to make the user know that the app is actually working.
Questions
In which position do I have to put it? If I write it in the ViewDidLoad, the AlertView is shown after those orrible few seconds. Is AlertView the best choice?
Thank you all.
add MBProgressHUD to your UIWebView viewDidLoad method
- (void)viewDidLoad {
[super viewDidLoad];
self.webView.delegate = self;
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading...";
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
[hud hide:YES];
}
UIAlerView is not a good idea.
//Delegate method of UIWebView
- (void)webViewDidStartLoad:(UIWebView *)webView
{
if(![[UIApplication sharedApplication] isNetworkActivityIndicatorVisible])
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
}
}
//Delegate method of UIWebView
- (void)webViewDidFinishLoad:(UIWebView *)webView_
{
if([[UIApplication sharedApplication] isNetworkActivityIndicatorVisible])
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
}
}
Set your tabBar as a delegate on viewDidLoad
- (void)viewDidLoad
{
[super viewDidLoad];
//Setting tabBarController as a delegate
self.tabBarController.delegate = self;
}
On your interface at .h method add delegate <UITabBarControllerDelegate>
Implement this function and add your loading view, for each time that you press tabBar. The method below gets executed always when the user taps an item on tabbar.
- (void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
{
//Show loading form here
[MBProgressHUD showHUDAddedTo:self.view animated:YES];
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// Do something...
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
}
P.S You need to add this method and set to delegate to every viewController which you need to show the alert view (Loading View)
For a loading view you can also load use MBProgressHUD as KingBabar pointed out. It has great features.
I am using MBProgressHUD to show a HUD but it is not showing as expected.
Steps:
User selects a cell in a tableView. Some data is parsed then a warning (UIAlertView) is shown to the user. The user is asked if they want to create a New Game with the selected (cell) user. Cancel/Start Game buttons.
The UIAlertView delegate methods is as follows:
pragma mark - UIAlertView Delegate
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(#"button %i",buttonIndex);
if (buttonIndex == 0) {
// Cancel button pressed
self.gameNewOpponentuser = nil;
} else {
// Start Game button pressed
[MESGameModel createNewGameWithUser:[PFUser currentUser] against:self.gameNewOpponentuser];
}
}
If the user selects Start Game the GameModel method is run. The game model(NSObject subclass) method is as follows:
pragma mark - Custom method
+(void)createNewGameWithUser:(PFUser *)user1 against:(PFUser *)user2 {
// First we put a HUD up for the user on the window
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithWindow:[UIApplication sharedApplication].keyWindow];
HUD.dimBackground = YES;
HUD.labelText = NSLocalizedString(#"HUDCreateNewGame", #"HUD - Create New Game text");
HUD.removeFromSuperViewOnHide = YES;
// Confirm we have two users to play with.
}
As you can see the HUD is alloc init to the keywindow of the application. However, the HUD is not showing as expected, nothing happens no UI lockup etc. I have put a breakpoint in the above method and can see that it is called however the HUD is not displayed
From the above, I expect the HUD to show up but nothing else happen, i.e. the HUD just remain on screen at the moment...
You are missing the part where you add the HUD to the Window and then showing the HUD. Note that you can add the HUD to the Window or the current view (self.view).
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithWindow:[UIApplication sharedApplication].keyWindow];
[[UIApplication sharedApplication].keyWindow addSubview:HUD]; //<-- You're missing this
HUD.dimBackground = YES;
HUD.labelText = NSLocalizedString(#"HUDCreateNewGame", #"HUD - Create New Game text");
HUD.removeFromSuperViewOnHide = YES;
[HUD showAnimated:YES whileExecutingBlock:^{ //<-- And this
// Do something
}];
Try this:
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:self.view];
HUD.labelText = NSLocalizedString(#"HUDCreateNewGame", #"HUD - Create New Game text");
// Set determinate mode
[HUD setMode:MBProgressHUDModeDeterminate];
[self.view addSubview:HUD];
[HUD show:YES];
You aren't adding it to any view for it to be displayed. So add it to appropriate view.
From the git hub project demo for MBProgressHUD
// The hud will dispable all input on the view (use the higest view possible in the view hierarchy)
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
You are not given the code to display the hud. use the following code
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.dimBackground = YES;
HUD.labelText = NSLocalizedString(#"HUDCreateNewGame", #"HUD - Create New Game text");
Ok, I found the issue. I was using clickedButtonAtIndex method for the UIAlertView which was of course holding to the view. I used didDismissWithButtonIndex instead and this works fine now.
I have a uitableview which loads data from internet and during this period im displaying MBProgressHUD. But the problem is the user cannot touch anything including previous page button before the table loads. Here is my codein two different classes:
//PROBLEM METHOD 1
- (void)viewDidLoad
{
[super viewDidLoad];
[tableEtkinlikler reloadData];
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = #"Açılıyor...";
HUD.userInteractionEnabled = NO;
[self performSelector:#selector(loadEtkinliklerTitlesAndImages) withObject:nil afterDelay:0];
tableEtkinlikler.dataSource = self;
tableEtkinlikler.delegate = self;
}
I have the same problem with a button also..In it im loading data from internet..
//PROBLEM METHOD 2
- (IBAction)AktivitelerButtonClicked:(UIButton *)sender
{
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = #"Açılıyor...";
HUD.userInteractionEnabled = NO;
[self performSelector:#selector(openAktivitelerWindow) withObject:nil afterDelay:0];
}
I believe for me that is the point of MBProgressHUD , it gives you the opportunity show a HUD while your tasks are completed, once your tasks are completed you dismiss it so user can interact with completed data.
However In some cases loading data takes so long so you might want to let the user decide to continue , choose another option or just simply go back
in your code this HUD.userInteractionEnabled = NO; should work but problem might be that showHUDAddedTo:self.view you do not use the highest view possible in the view hierarchy.
Try to use this:
- (IBAction)showSimple:(id)sender {
// The hud will dispable all input on the view (use the higest view possible in the view hierarchy)
HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.userInteractionEnabled = NO;
// Regiser for HUD callbacks so we can remove it from the window at the right time
HUD.delegate = self;
// Show the HUD while the provided method executes in a new thread
[HUD showWhileExecuting:#selector(loadEtkinliklerTitlesAndImages) onTarget:self withObject:nil animated:YES];
}
I'm trying to show a mix of HUDs in my app, so for example, when a user taps "login", I want my HUD to display the spinner saying "logging in...", then change to a checkmark image saying "logged in!", then hide. I'm trying to accomplish this using the following code:
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = #"Logging in";
\\Do network stuff here, synchronously (because logging in should be synchronous)
\\ Then upon success do:
HUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmark.png"]];
HUD.mode = MBProgressHUDModeCustomView;
HUD.labelText = #"Logged in!";
sleep(2);
[MBProgressHUD hideHUDForView:self.view animated:YES];
Problem here, is that the sleep(2) is being applied to the initial spinner, and not the checkmark HUD. So the spinner is showing for a longer period of time, and the checkmark disappears after a split second. How can I do it so that the checkmark stays there for a longer time before the HUD hides?
Thanks!
As best practice, don't use sleep. Try using the "performSelector:withObject:afterDelay" method. Create a method that does the
[MBProgressHUD hideHUDForView:self.view animated:YES];
action and call it after a predefined delay of your choice. Don't forget that you're handling UI so make sure you're calling this on the main thread.
I would create two HUDs. The first one for the "waiting" part, and the second one for the success. Start the loadingHUD before your network task, and hide it when you are done :
MBProgressHUD *loadingHUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
loadingHUD.mode = MBProgressHUDModeIndeterminate;
loadingHUD.labelText = #"Please wait...";
loadingHUD.detailsLabelText = #"Connection in progress";
[loadingHUD show:YES];
// Do the network stuff here
[loadingHUD hide:YES];
Right after that, to notify the success, create the successHUD as you want it, and hide it after delay :
MBProgressHUD *successHUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
successHUD.mode = MBProgressHUDModeCustomView;
successHUD.customView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"checkmark.png"]];
successHUD.labelText = #"Logged in !";
[successHUD show:YES];
[successHUD hide:YES afterDelay:2.0];
Your success HUD will show up for 2 seconds, and hide automatically.
That's how I always use MBProgressHUD.