How to present the same modalView after dismissing it once - cocoa-touch

I'm having some problem trying to present a modal view controller after it has been presented the first time, so I just start a little test method, it presents, dismisses and presents again the same controller modally.
// This is just test Code.
MYViewController *vc = [[MYViewController alloc] init];
[self presentModalViewController:vc animated:YES];
[self dismissModalViewControllerAnimated:YES];
[self presentModalViewController:vc animated:YES];
I get the error:
2011-11-15 09:50:42.678 Proyecto3[1260:11603] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally an active controller <RootViewController: 0x6d9d090>.'
The documentation does not add any clue here.

#David, make the MYViewController an instance variable, and initialize it like this:
if (myInstance==nil)
//create instance of MYViewController
//myInstance.delegate=self
//present modal VC
In MYViewController create a protocol to co-ordinate dismissing MYViewController may be on a done or cancel button. In the button action call some thing like
done
{
if([delegate respondsToSelector:#selector(willDismissModalView)])
{
[delegate willDismissModalView];
}
}
and in willDismissModalView method of your VC dismiss MYViewController. This way you can do it 'n' times.

In your code [self dismissModalViewControllerAnimated:YES];
will do nothing to the modalViewController,since here the "self" is the viewController from where you are trying to present a modalViewController "vc".Again you are presenting a modalViewController which is already presented.this will result in a termination.
You can dismiss the modalViewController vc in that viewController,here vc.

You cannot present/dismiss the view controller while it is animated, I think this works
MYViewController *vc = [[MYViewController alloc] init];
[self presentModalViewController:vc animated:NO];
[self dismissModalViewControllerAnimated:NO];
[self presentModalViewController:vc animated:YES];
But I don't really see any reason for doing it, why would you want to dismiss and re-present a modal view controller which is already presented?

Related

dismissViewControllerAnimated shows a white screen

I got a ViewController named viewControllerA which presents ViewControllerB with this common code:
ViewControllerB *viewControllerB = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:nil] instantiateViewControllerWithIdentifier:#"Identifier"];
[self presentViewController:viewControllerB animated:YES completion:nil];
viewControllerB is a UITabbarController but when calling
[self dismissViewControllerAnimated:YES completion:nil];
from one of the TabbarControllers ViewControllers (say page 2) I just get a white screen. I was expecting to see viewControllerA but instead I just see an empty screen.
also
NSLog(#"%#", [[self presentingViewController] class]);
logs viewControllerA.
Does anybody know why I get a empty white screen instead of viewControllerA?
EDIT
I put a breakpoint on viewDidAppear in viewControllerA and it does break there when calling dismissViewControllerAnimated. But how is it possible that it only shows a white screen?
Also I think you should know that viewControllerA's layout is created programatically. viewControllerB is created much later and in the meantime we decided to use a storyboard so viewControllerB's layout is created with storyboard.
you can remove your self (in viewcontrollerB)
[self.presentingViewController dismissViewControllerAnimated:NO completion:nil];
If you present viewControllerB from viewControllerA using
[self presentViewController:viewControllerB animated:YES completion:nil];
you should probably call [self dismissViewControllerAnimated:YES completion:nil]; in the viewControllerA, but not from one of the of the pages of your TabBarViewController.

close modalvew controller and parentview

I am trying to open a modalview from a view like that,
SignupViewController *signUpView = [[SignupViewController alloc] initWithNibName:#"SignupViewController" bundle:nil];
[signUpView setModalTransitionStyle:UIModalTransitionStyleCrossDissolve];
self.parentViewController.view.transform = CGAffineTransformMakeScale(1.3, 1.3);
self.parentViewController.view.alpha = 0;
[UIView animateWithDuration:.35 animations:^{self.parentViewController.view.alpha = 1.0; self.parentViewController.view.transform = CGAffineTransformMakeScale(1, 1);}];
[self presentModalViewController:signUpView animated:YES];
After login i am closing the modalview and redirecting to anther view, but the parentview is still there,
[self dismissViewControllerAnimated:YES completion:^{
ToolsViewController *gototoolpage = [[ToolsViewController alloc] initWithNibName:#"ToolsViewController" bundle:nil];
[self.navigationController pushViewController:gototoolpage animated:YES];
}
How to dismiss the parentview also. Any idea
You code looks a little confused. What do you intend by references to parentViewController? Check the docs - it is the containing viewController, not the previous or presenting viewController. In a NavigationController context this would be the UINavigationController. In a modal view context there is no parentViewController, but there is a presenting ViewController. I am not sure what you intend by all of those calls to self.parentViewController.
In any case you should really be sending the dismiss request back to your presenting viewController via a delegate so that it is completely clear where the pushViewController message is being passed from and to.
In the header file of your signUpViewController declare a protocol:
#protocol SignUpViewControllerDelegate
- (void) dissmissSignUpVC;
#end
then in your presentingViewController, after
SignupViewController *signUpView = [[SignupViewController alloc] initWithNibName:#"SignupViewController" bundle:nil];
add
[signUpView setDelegate:self];
and implement the delegate method with the same code you now have in your completion block:
- (void) dissmissSignUpVC {
ToolsViewController *gototoolpage = [[ToolsViewController alloc]
initWithNibName:#"ToolsViewController" bundle:nil];
[self.navigationController pushViewController:gototoolpage animated:YES];
}
In signUpView invoke the delegate's method to dismiss:
[[self delegate] dissmissSignUpVC];
Watch out for those stacked animations, I suspect that only the first will be performed (i.e. gototollpage animated:YES might as well be gototoolpage animated:NO)
Perhaps anyway you should reconsider your logic. I imagine the user might have a confusing experience if you do this under-the-hood manipulation of viewControllers. Better that there is a UI control for the user to navigate to toolsViewController so they understand where they are?

Attempt to present * on * whose view is not in the window hierarchy

I'm trying to make a modal view controller in my app delegate (I created a function called showLoginView). But whenever I try to call it I get a warning in XCode:
Warning: Attempt to present <PSLoginViewController: 0x1fda2b40> on <PSViewController: 0x1fda0720> whose view is not in the window hierarchy!
Here's the method code:
- (void)showLoginView
{
PSLoginViewController *loginViewController = [[UIStoryboard storyboardWithName:#"MainStoryboard" bundle:NULL] instantiateViewControllerWithIdentifier:#"PSLoginViewController"];
[self.window.rootViewController presentViewController:loginViewController animated:NO completion:nil];
}
How can I add the view to the window hierarchy? Or maybe I'm doing something very wrong?
You can't display a modal view controller from the appDelegate. You need to display a modal ViewController from whichever viewController is currently displaying full-screen. In other words, you need to put that code into your root view controller, or whichever one you want to display the modal vc from...
Also, you'll want to use the method "presentModalViewController" to present the modal. You can set properties on the modal vc such as:
vC.modalPresentationStyle = UIModalPresentationFormSheet;
vC.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:vC animated:YES];
You can actually present a modal view Controller from the AppDelegate as long as you detect the current visible viewController and take care of the case where you current controller is a navigationController.
Here is what I do:
UIViewController *activeController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]]) {
activeController = [(UINavigationController*) activeController visibleViewController];
}
[activeController presentModalViewController:loginViewController animated:YES];
UIViewController *activeController = [UIApplication sharedApplication].keyWindow.rootViewController;
if ([activeController isKindOfClass:[UINavigationController class]])
{
activeController = [(UINavigationController*) activeController visibleViewController];
}
else if (activeController.modalViewController)
{
activeController = activeController.modalViewController;
}
[activeController presentModalViewController:vc animated:YES];
I ran into this problem on iOS 7 - the key to making any of the proposed solutions work was to call
[self.window makeKeyAndVisible];
in your AppDelegate.
After that call, presenting a modal view from the window's rootViewController worked.
Another reason for that warning can be that you want to present a view controller from an instance which is not the top most view controller.
So first you have to get the topmost UIViewController and using this instance to call presentViewController:
UIViewController *root = [UIApplication sharedApplication].keyWindow.rootViewController;
while (root.presentedViewController) {
root = root.presentedViewController;
}
You can NSLog(#"%#", self.window.rootViewController), and see what the rootViewController really is.
I came into this problem, when the rootViewController is a normal UIViewController.
Replace it with a UINavigationController, wish it will help.
Faced this issue while trying to present controller from the call of delegate of other controller . i.e : show search filter with delegate , once done back to my controller and receive data via the delegate then present controller , all I had to do is to dispatch the present code cause while in a delegate you're in another thread , that's why you're presenting on your view from main thread another controller from that other thread , so have to go back to main thread , just put the presenting code like this :
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:searchVC animated:true completion:nil];
});
Hope this helps !

Identify when I return to ViewController

I have two views with their controllers.
The application start with the FirstViewController.
Then with a button I open the SecondViewController.
With other button I dismiss the SecondViewController to return to FirstViewController.
Is there any way to detect that in FirstViewController that it has recovered focus?
EDIT: Ok I look the answers and I use viewWillAppear but don't work if I use a UIModalPresentationFormSheet.
-(IBAction)openSecondView{
SecondViewController *screen = [[SecondViewController alloc] initWithNibName:nil bundle:nil];
screen.modalTransitionStyle = UIModalTransitionStyleCrossDissolve;
screen.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:screen animated:YES];
[screen release];
}
And close this view with close button.
viewWillAppear never called.
implement UIViewController's viewWillAppear method
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
}
Add a delegate (Protocol) method. Call the delegate method just before dismissing the SecondViewController.
For more info on delegate, see delegate
implementation of the viewWillAppear: callback will work only if you use navigation controller or tabbarcontroller for displaying another controller + with such approach you will need to check somehow if this is only the first appearance of the view, or was called by any other reason;
using a delegate, as described by Gomathi above is a much better option!
Depends how you are setting up the first view controller. Encapsulate it within UINavigationViewController (and if you do not want the navigation bar, you can always set it to hidden ( [self.navigationController.navigationBar setHidden:YES] ). ViewWillAppear will work then.
inside the viewWillAppear of the FirstViewController
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
NSLog(#"view 1 focused");
}

"EXC_BAD_ACCESS" in switching to another view

I have MainMenuViewController with button which action is
- (IBAction) goToFirstView {
FirstViewController *fvc = [[FirstViewController alloc] init];
[self.view addSubview:fvc.view];
[fvc release];
}
FirstViewController have UIButton with action
- (IBAction) rightArrow {
SecondViewController *svc = [[SecondViewController alloc] init];
[self.view addSubview:svc.view];
[svc release];
}
But when I press "rightArrow" button app crashes with "EXC_BAD_ACCESS". Can't found my problem. Help me please.
[svc release];
The problem is here. When releasing the view controller, the view's events will target a freed object, and make your program crash (probably in viewDidLoad or viewDidAppear if it's instant but it doesn't matter). Note that a view does not (normally, AFAIK) retain it's view controller, if that might have been your assumption...
When you say [self.view addSubview:svc.view] you're adding SecondViewController's view to FirstViewController's view. Similar with MainViewController and FirstViewController. What you'll end up with is a view hierarchy that looks like this:
main view
first view
second view
I doubt that's really what you want. Instead, use a navigation controller with your MainViewController as the nav controller's root controller, and then use -pushViewController:animated: to push the controllers (not the views!) onto the navigation stack.