([NSThread isMainThread]) exception MBProgressHUD - ios

I have been getting this error when trying to present the contact book view controller after a long process is complete.
Here is how I am running the process and showing the HUD.
HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = #"Scanning...";
UIFont *font = [UIFont fontWithName:#"HelveticaNeue-Light" size:20];
[HUD setLabelFont:font];
[HUD showWhileExecuting:#selector(scanWithImage:) onTarget:self withObject:_image animated:YES];
Here is how I am hiding the HUD and showing the contact book vc, which is called in scanImage.
dispatch_async(dispatch_get_main_queue(), ^(void){
[HUD hide:YES];
});
[self presentViewController:navigation animated:YES completion:nil];
Can anyone tell me how to resolve this issue?
Full error message:
Assertion failed: ([NSThread isMainThread]), function -[AFContextManager addContextProvider:], file /SourceCache/MobileAssistantFramework/MobileAssistantFramework-651.49/AFContextManager.m, line 113.

I guess you are trying to present the new controller from the background thread. Anything with respect to User interface including presenting a new controller cannot be performed from the background thread. So present the new controller from the main thread. Try this
dispatch_async(dispatch_get_main_queue(), ^(void){
[HUD hide:YES];
[self presentViewController:navigation animated:YES completion:nil];
});

Related

View controller not being created

I have a navigation controller that starts my app (rootViewController is the navigationController). Then inside one of the views of the navigation I call:
TabBarController *tab = [[TabBarController alloc] init];
// Presentation
[self presentViewController:tab animated:NO completion:nil];
Then one of the tabs calls the UIImagePickerController and then saves the image on another thread. Then I return to the main queue and run:
dispatch_async(dispatch_get_main_queue(), ^{
[picker dismissViewControllerAnimated:YES completion:nil];
PostViewController *post = [[PostViewController alloc] init];
// Presentation
[self presentViewController:post animated:NO completion:nil];
});
But the post view never gets called and the viewDidLoad never gets hit in the PostViewController.m. Instead the imagePicker disappears and returns to the tabBarController. How do I fix this?
Assuming that you PostViewController object is not nil , Present the view controller after the dismiss process of picker ViewController is completed . Try this Code
dispatch_async(dispatch_get_main_queue(), ^{
[picker dismissViewControllerAnimated:YES completion:^{
PostViewController *post = [[PostViewController alloc] init];
// Presentation
[self presentViewController:post animated:NO completion:nil];
}];
});

NSThread isMainThread execption

Can someone explain to me what this error message is saying?
Assertion failed: ([NSThread isMainThread]), function -[AFContextManager addContextProvider:], file /SourceCache/MobileAssistantFramework/MobileAssistantFramework-651.49/AFContextManager.m, line 113.
This only happens about 1/8 of running my application.
I believe the last line of code in this snippet is causing it.
HUD = [[MBProgressHUD alloc]initWithView:self.view];
HUD.labelText = #"Scanning..";
[self.view addSubview:HUD];
[HUD showWhileExecuting:#selector(scanWithImage:) onTarget:self withObject:image animated:YES];
Force it to run on the main thread. Like this
dispatch_sync(dispatch_get_main_queue(), ^{
HUD = [[MBProgressHUD alloc]initWithView:self.view];
HUD.labelText = #"Scanning..";
[self.view addSubview:HUD];
[HUD showWhileExecuting:#selector(scanWithImage:) onTarget:self withObject:image animated:YES];
});
This assumes that the UI is not blocking while executing the above code and that you ABSOLUTELY want it to be synchronous. Else its very safe to use dispatch_async
Try this:
HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = #"Scanning..";
[HUD showWhileExecuting:#selector(scanWithImage:) onTarget:self withObject:image animated:YES];
Hope this helps.. :)
The addContextProvider: method is asserting that it is being run on the main thread. Likely the issue is that a method you must call from the main thread is being called from a background thread.

Add Subview to SKStoreProductViewController

I am trying to counteract Apple's lack of innovation with SKStoreProductViewController. Im pretty certain this isn't possible, but lets see.
I am presenting the view controller outside of this block:
[storeController loadProductWithParameters:productParameters completionBlock:^(BOOL result, NSError *error) {}];
[self presentViewController:storeController animated:YES completion:^{}];
That way the view controller shows immediately, rather than waiting 30 seconds to load the product. However, now it obviously shows a blank controller while it loads the product. I would love to put a UIActivityIndicatorViewon the window until the product loads. I have tried this:
[self presentViewController:storeController animated:YES completion:^{
UIActivityIndicatorView *activity = [[UIActivityIndicatorView alloc]initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
[activity startAnimating];
activity.frame = CGRectMake(0, 0, 44, 44);
activity.center = self.view.window.center;
[self.view.window addSubview:activity];
}];
No luck there. Is it possible to add an activity indicator on top of this view?
Have you tried to bring up the view controller once the SKStoreProductViewController loading is completed? like this
[storeController loadProductWithParameters:productParameters completionBlock:^(BOOL result, NSError *error)
{
[self presentViewController:storeController animated:YES completion:^{}];
}];

MBProgressHUD blocks user interaction with a tableview

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];
}

MBProgressView sleep for mixed views

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.

Resources