I am displaying a HUD while populating the TableView, but it seems to be showing behind the TableView (tableview separator breaking the hud).
Here's the code in the TableViewController:
- (void)viewDidLoad {
[super viewDidLoad];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeText;
hud.labelText = #"Loading";
// Populate the table
[self getTableData];
self.tableView.rowHeight = 90;
}
It's doing this only with TableViews.
The issue here is that you are adding the HUD when the view loads, which is likely before your tableView has been displayed, so the tableView is created and appears to cover the HUD. Move that code into viewDidAppear and your problem will go away:
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeText;
hud.labelText = #"Loading";
}
Use self.navigationController.view instead of self.view if you want to implement in viewDidLoad
Include this:
#import <QuartzCore/QuartzCore.h>
You can use layer.zPosition to order the visibility of your objects/views.
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeText;
hud.labelText = #"Loading";
hud.layer.zPosition = 2;
self.tableView.layer.zPosition = 1;
The higher zPosition value, more priority in display.
Even in ViewDidLoad also we can handle it like this::
- (void)viewDidLoad {
[super viewDidLoad];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading..";
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[self contactsFromAddressBook];
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
[self.tableView reloadData];
});
});
}
Related
In the following example, I am presenting a UIViewController that has a UIStackViewController as its child:
UIViewController *splitViewParentVC = UIViewController.new;
UIViewController *masterVC = UIViewController.new;
UIViewController *detailVC = UIViewController.new;
UISplitViewController *splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = #[masterVC, detailVC];
[splitViewParentVC addChildViewController:splitViewController];
[splitViewParentVC.view addSubview:splitViewController.view];
[splitViewController didMoveToParentViewController:splitViewParentVC];
splitViewController.view.frame = splitViewParentVC.view.bounds;
splitViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
__weak UISplitViewController *wSplitViewController = splitViewController;
[self presentViewController:splitViewParentVC animated:YES completion:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (wSplitViewController) {
NSLog(#"the split view controller has leaked");
} else {
NSLog(#"the split view controller didn't leak");
}
});
}];
});
In iOS 9 and 9.1, the above code will print the split view controller has leaked, indicating that the UIStackViewController has leaked (more importantly, it leaks its master and detail view controllers as well).
Yes, a retain cycle bug has been confirmed to exist in iOS 9 by Apple Staff.
I've tested that the retain cycle does not exist in iOS 8.4, but does exist in iOS 9.0 and 9.1. The leak seems to be fixed as of iOS 9.2 (tested in Xcode 7.2 beta 2 on the iOS 9.2 Simulator) I've put together a sample project to easily confirm whether or not UISplitViewController causes itself to leak (just run it and check the console output).
This also tests an attempt to allow the master and detail view controllers to be deallocated. As one can see, the master view controller still seems to be retained by the UISplitViewController even after it is removed from the UISplitViewController.viewControllers array property.
Here is the code from the sample project:
- (void)viewDidLoad {
[super viewDidLoad];
[self testSplitViewControllerRetainCycleWithCompletion:^{
[self testManuallyFreeingUpMasterAndDetailViewControllers];
}];
}
- (void)testSplitViewControllerRetainCycleWithCompletion:(void (^)())completion {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIViewController *splitViewParentVC = UIViewController.new;
UIViewController *masterVC = UIViewController.new;
UIViewController *detailVC = UIViewController.new;
UISplitViewController *splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = #[masterVC, detailVC];
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
splitViewController.preferredPrimaryColumnWidthFraction = 0.3125; // 320 / 1024
splitViewController.minimumPrimaryColumnWidth = 100;
[splitViewParentVC addChildViewController:splitViewController];
[splitViewParentVC.view addSubview:splitViewController.view];
[splitViewController didMoveToParentViewController:splitViewParentVC];
splitViewController.view.frame = splitViewParentVC.view.bounds;
splitViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
__weak UISplitViewController *wSplitViewController = splitViewController;
__weak UIViewController *wMaster = masterVC;
__weak UIViewController *wDetail = detailVC;
[self presentViewController:splitViewParentVC animated:YES completion:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (wSplitViewController) {
NSLog(#"the split view controller has leaked");
} else {
NSLog(#"the split view controller didn't leak");
}
if (wMaster) {
NSLog(#"the master view controller has leaked");
} else {
NSLog(#"the master view controller didn't leak");
}
if (wDetail) {
NSLog(#"the detail view controller has leaked");
} else {
NSLog(#"the detail view controller didn't leak");
}
completion();
});
}];
});
});
}
- (void)testManuallyFreeingUpMasterAndDetailViewControllers {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIViewController *splitViewParentVC = UIViewController.new;
UIViewController *masterVC = UIViewController.new;
UIViewController *detailVC = UIViewController.new;
UISplitViewController *splitViewController = [[UISplitViewController alloc] init];
splitViewController.viewControllers = #[masterVC, detailVC];
splitViewController.preferredDisplayMode = UISplitViewControllerDisplayModeAllVisible;
splitViewController.preferredPrimaryColumnWidthFraction = 0.3125; // 320 / 1024
splitViewController.minimumPrimaryColumnWidth = 100;
[splitViewParentVC addChildViewController:splitViewController];
[splitViewParentVC.view addSubview:splitViewController.view];
[splitViewController didMoveToParentViewController:splitViewParentVC];
splitViewController.view.frame = splitViewParentVC.view.bounds;
splitViewController.view.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
__weak UIViewController *wMaster = masterVC;
__weak UIViewController *wDetail = detailVC;
[self presentViewController:splitViewParentVC animated:YES completion:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:nil];
splitViewController.viewControllers = #[UIViewController.new, UIViewController.new];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (wMaster) {
NSLog(#"the master view controller has STILL leaked even after an attempt to free it");
} else {
NSLog(#"the master view controller didn't leak");
}
if (wDetail) {
NSLog(#"the detail view controller has STILL leaked even after an attempt to free it");
} else {
NSLog(#"the detail view controller didn't leak");
}
});
});
});
}
UPDATE: The leak seems to be fixed as of iOS 9.2 (tested in Xcode 7.2 beta 2 on the iOS 9.2 Simulator)
As I know -[UIViewController addChildViewController:] has memory leak problem in iOS 9.0~9.1. So I think it not UISplitViewController's fault only. Snipets as follows,
- (void)viewDidLoad {
[super viewDidLoad];
MyFirstViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyFirstViewController"];
[self addChildViewController:vc];
[self.view addSubview:vc.view];
[vc didMoveToParentViewController:self];
}
You will find that MyFirstViewController's dealloc has NOT be called if you retreat from the current view controller.
A possible workaround is use storyboard's Container View instead of addChildViewController in code. I'm comfirmed that Container View's child view controller will be released properly.
Another workaround is removeChildViewController: in -(void)viewDidDisappear:(BOOL)animated. However, as apple's staff mentioned, this workaround is not recommended.
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
NSArray<__kindof UIViewController *> * children = self.childViewControllers;
for (UIViewController *vc in children) { // not recommended
[vc willMoveToParentViewController:nil];
[vc.view removeFromSuperview];
[vc removeFromParentViewController];
}
}
When a user presses the center UITabBarItem I present a modal UIView. Think of it like Instagram.
-(BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController{
if(viewController == [self.viewControllers objectAtIndex:2])
{
CameraViewController *cameraVC = [[UIStoryboard storyboardWithName:#"Main" bundle:nil] instantiateViewControllerWithIdentifier:#"cameraVC"];
UINavigationController* navController = [[UINavigationController alloc] initWithRootViewController:cameraVC];
navController.navigationBar.barStyle = UIStatusBarStyleLightContent;
[self presentViewController:navController animated:YES completion:nil];
return NO;
}
else
{
return YES;
}
}
This works perfectly.
When I'm done taking a picture in CameraViewController I want the view to be dismissed and the 4th UITabBarItem to be selected for the results of the picture (HistoryViewController).
This is how I do that in CameraViewController (who is modally pushed):
[self dismissViewControllerAnimated:YES completion:nil];
[(UITabBarController *)self.presentingViewController setSelectedIndex:3];
And this is where it gets buggy.
As you can see the text in the 4th tab is selected, but the first tab icon is still selected. Also the presented view is the one from the first tab.
After 10 seconds or so it eventually changes the view to the, correct, 4th tab.
I'm trying to find out what process creates this slowdown so I've set up a lot of NSLog's.
The approximate 10 second slowdown is between [(UITabBarController *)self.presentingViewController setSelectedIndex:3]; in the CameraViewController and viewDidLoad in HistoryViewController.
What is happening in between these calls/methods that could cause the slowdown?
Edit:
In CameraViewController:
- (void)scan {
dispatch_queue_t scanTesseract = dispatch_queue_create("scanTesseract", NULL);
dispatch_async(scanTesseract, ^(void) {
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD setForegroundColor:[UIColor ht_mintDarkColor]];
[SVProgressHUD showProgress:0 status:#"Scanning"];
});
//background processing goes here
[self.tesseract setImage:self.imgToScan.blackAndWhite];
[self.tesseract recognize];
[self filterResults:[self.tesseract recognizedText]];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
});
[self scanningDone];
});
}
- (void)scanningDone {
[LastScan getInstance].hasBeenViewed = FALSE;
[self dismissViewControllerAnimated:YES completion:nil];
[(UITabBarController *)self.presentingViewController setSelectedIndex:3];
}
In HistoryViewController:
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(#"ViewDidLoad");
self.navigationController.navigationBar.barStyle = UIStatusBarStyleLightContent;
self.collectionView.backgroundColor = [UIColor whiteColor];
}
You are calling your scanningDone from within a background queue. Execute that method on the main Queue.
dispatch_async(dispatch_get_main_queue(), ^{
[self scanningDone];
});
What about this?
[self dismissViewControllerAnimated:YES completion:^{
[(UITabBarController *)self.presentingViewController setSelectedIndex:3];
}];
I am using this api to share an image , but it crashes many many times !!! my code is runs fine but sometimes MBProgressHUD causes app crashing , am I using this API right ?
- (void)shareOther {
HUD = [[MBProgressHUD alloc] initWithView:self.view];
[self.view addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Loading";
[HUD showWhileExecuting:#selector(capture) onTarget:self withObject:nil animated:YES];
}
- (void)capture {
//capture view
UIGraphicsBeginImageContextWithOptions(Sview.bounds.size, Sview.opaque, 0.0);
[Sview.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage * screenshot = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSString *str = [NSString stringWithFormat:#"some string"];
UIActivityViewController* activityViewController =
[[UIActivityViewController alloc] initWithActivityItems:#[screenshot , str ]
applicationActivities:nil];
UIViewController *vc = self.view.window.rootViewController;
[vc presentViewController: activityViewController animated: YES completion:nil];
}
- (void)hudWasHidden:(MBProgressHUD *)hud {
// Remove HUD from screen when the HUD was hidded
[HUD removeFromSuperview];
HUD = nil;
}
This is how I use it:
[MBProgressHUD showHUDAddedTo: self.view animated: YES];
[self doVeryLongTask];
[MBProgressHUD hideHUDForView: self.view animated: YES];
Try to present this after removing HUD.
- (void)hudWasHidden:(MBProgressHUD *)hud {
// Remove HUD from screen when the HUD was hidded
[HUD removeFromSuperview];
HUD = nil;
UIViewController *vc = self.view.window.rootViewController;
[vc presentViewController: activityViewController animated: YES completion:nil];
}
I'm trying to show an image on MBProgressHUD by using this code
MBProgressHUD *HUD = [[MBProgressHUD alloc] initWithView:self.navigationController.view];
[self.navigationController.view addSubview:HUD];
HUD.customView = [[[UIImageView alloc] initWithImage:[UIImage imageNamed:#"37x-Checkmark"]] autorelease];
HUD.mode = MBProgressHUDModeCustomView;
HUD.labelText = #"تم إرسال وزنك بنجاح";
[HUD show:YES];
[HUD hide:YES afterDelay:1.5];
but this is what i get
what is the problem ?
I tried your code and it is working for me.
In MBProgressHud.h file, in comments it is mentioned that
/**
* The UIView (i.g., a UIIMageView) to be shown when the HUD is in MBProgressHUDModeCustomView.
* For best results use a 37 by 37 pixel view (so the bounds match the build in indicator bounds).
*/
So perhaps the image you used is missing or is noyt included. Please check that.
You must include the images from this link in your xcode project, seems you didn't do so.
That's because you don't have the image named "37x-Checkmark" in your project. Just add the 37x-Checkmark.png image file in your project.
Now it is changed with IOS &
-(IBAction)save:(id)sender
{
HUD = [[MBProgressHUD alloc] initWithWindow:[[UIApplication sharedApplication] keyWindow]];
[[[UIApplication sharedApplication] keyWindow] addSubview:HUD];
HUD.delegate = self;
HUD.labelText = #"Initiating Entropy...";
HUD.minSize = CGSizeMake(135.f, 135.f);
[HUD showWhileExecuting:#selector(myTask) onTarget:self withObject:nil animated:YES];
}
-(void)myTask
{
sleep(6);
UIView *v = [[UIView alloc] init];
UIImageView *img = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"check_mark_green"]];
[v setFrame:CGRectMake(0, 0, img.frame.size.width, img.frame.size.height)];
v.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"37x-Checkmark"]];
HUD.mode = MBProgressHUDModeCustomView;
HUD.customView = v;
HUD.labelText = nil;
HUD.detailsLabelText = #"Geo-Location Cordinates Secured";
}
try this and enjoy the code.
I am dismissing one modal view controller and then immediately presenting another modal view controller however I cannot currently use animation on both of them only the second one.
Is there anyway to delay the process so that the user experiences both animations?
The code below currently works however user only sees the second animation obviously:
// First one configure
detailViewController.modalPresentationStyle = UIModalPresentationFullScreen;
detailViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:detailViewController animated:YES];
//Dismiss first one
[self dismissModalViewControllerAnimated:NO];
//Immediately configure and show second one
navController.modalPresentationStyle = UIModalPresentationFormSheet;
navController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navController animated:YES];
There is now a completetion block available in present modal view controller. See this LINK. This is available in iOS5.0 +.
This has the advantage that you don't need to estimate the timer delay if you were to use a timer solution.
Just put the code for your second animation in the block:
//Block safe reference to self to prevent retain cycles
__block typeof (self) selfReference = self;
// First one configure
detailViewController.modalPresentationStyle = UIModalPresentationFullScreen;
detailViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:detailViewController animated:YES completion:
^{
//Dismiss first one
[selfReference dismissModalViewControllerAnimated:NO];
//Immediately configure and show second one
navController.modalPresentationStyle = UIModalPresentationFormSheet;
navController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[selfReference presentModalViewController:navController animated:YES];
}];
Make a selector that does the following:
- (void)showSecondModalVC {
//Dismiss first one
[self dismissModalViewControllerAnimated:NO];
//Immediately configure and show second one
navController.modalPresentationStyle = UIModalPresentationFormSheet;
navController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navController animated:YES];
}
An then in the main piece of code:
// First one configure
detailViewController.modalPresentationStyle = UIModalPresentationFullScreen;
detailViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:detailViewController animated:YES];
[self performSelector:#selector(showSecondModalVC)
withObject:nil
afterDelay:0.5f];
You will have to look closely and see how much it takes for the first modal to show so the animations look good.
You can do it in some other style.
detailViewController.modalPresentationStyle = UIModalPresentationFullScreen;
detailViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:detailViewController animated:YES];
[self dismissModalViewControllerAnimated:NO];
[self performSelector:#selector(someFunction) withObject:nil afterDelay:1.0];
- (void) someFunction{
//Immediately configure and show second one
navController.modalPresentationStyle = UIModalPresentationFormSheet;
navController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navController animated:YES];
}
Try with this:
// First one configure
detailViewController.modalPresentationStyle = UIModalPresentationFullScreen;
detailViewController.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:detailViewController animated:YES];
//Dismiss first one
[self dismissModalViewControllerAnimated:NO];
NSTimer Timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(openSecondView) userInfo:nil repeats:NO];
-(void)openSecondView
{
//Immediately configure and show second one
navController.modalPresentationStyle = UIModalPresentationFormSheet;
navController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
[self presentModalViewController:navController animated:YES];
}
Happy Coding...