Let me start by saying that I want to do all of this without storyboards. My situation is as follows: I have a login page for the user. On the login page the user can login, or be taken to a register page. When the user registers successfully, they are taken to the main menu. However, if I they click logout from the main menu, dismissing the main menu viewcontroller takes me back to the register page (which makes sense).
The problem is I need to take the user to the login page. However, I cannot present the original login page because I get the error "Warning: Attempt to present on which is already presenting "
Is there some sort of way for me to dismiss 2 view controllers? Or maybe run a line of a code on a view controller if it is presented from a certain view controller?
Thank you!
This is the solution that I used from the main menu and it worked
if(self.presentingViewController == registerPage){
registerPage.dismissViewControllerAnimated(true, completion: nil)
}
self.dismissViewControllerAnimated(true, completion: nil)
If you are using a navigation controller, you can get the viewControllers array property, build up a new array without the viewControllers you want to dismiss and set the property with the new array.
No need to switching to Navigation controllers.
If you are presenting view controller than simply try this:
For Objective-C:
[[[self presentingViewController] presentingViewController] dismissViewControllerAnimated:YES completion:nil];
For Swift:
self.presentingViewController!.presentingViewController!.dismissViewControllerAnimated(true, completion: {})
Hope it will work for you.
Related
I know this is probably an easy task but I can't get my head around how to solve it. I'm not using a NavigationController by the way. Basically I have 3 view controllers in my app:
-LoginVC (this has a register button. when tapped, it goes to the SignupVC)
-SignupVC (if user signs up, it will push to the MainAppVC)
-MainAppVC (has a logout button)
For the transitions, I use the method: present(viewController, animated:, completion:)
When the user logs in via the LoginVC, he'll be presented the MainAppVC as expected. When he logs out, I'll dismiss the current VC (which is the MainAppVC) to send him back to the LoginVC.
Now here is the case where I have questions about. When the user does not have an account and signs up, this is the VCs he will pass through (LoginVC > SignupVC > MainAppVC). Once he registers successfully, he'll be presented the MainAppVC. Now once he logs out, he'll be transitioned from the MainAppVC to the SignupVC because I used the same dismiss method.
What I want to do is to send the user back to the LoginVC from the MainAppVC. How do I accomplish that without using a navigation controller in my project? How do popular apps (Facebook, Twitter, Instagram, etc) handle this in their apps?
One way I can think of is to perform a segue from 3rd to 1st VC but I think that's a dirty way since it just adds to the stack, instead of popping it off which is a potential issue.
There are three solutions.
1.Use window root view controller.Set root view controller between login and main view controller.
let loginSb = UIStoryboard(name: "Login", bundle: nil)
let loginVc = loginSb.instantiateInitialViewController()
window!.rootViewController = loginVc
2.Dismiss the sign up view controller the first time you present main view controller.And present the main view controller from login view controller.
3.Try to dismiss the sign up view controller in the completion block when you dismiss the main view controller.
I think the first one is best.
If are forced to NOT use a rootViewController (with UINavigationController), you should check in MainAppVC which one is the previous VC (Pseudocode in the example) and do:
Note: Swift 3 Code.
// If previous VC is LoginVC
dismiss(animated: true)
// else if previous VC is SignupVC
// here's what you want
presentingViewController?.presentingViewController?.dismiss(animated: true)
Hope that helped.
Another option is to use unwind segues.
In the ViewController that you want to go back to, implement a method as follows
#IBAction func unwindToLogin(segue: UIStoryboardSegue) {
}
Then in Storyboard, from the last ViewController right click and drag from the button (for example) to Exit and choose unwindToLoginWithSegue.
Note that you can also do this programatically. Here is a good tutorial.
Best approach is :
Dismiss SignUpVC once User Register successfully, Then from LoginVC present MainVC.
In SignupVC :
self.dismissViewControllerAnimated(true, completion: {
self.LoginVC!.present(MainVC, animated: true, completion:nil)
})
This approach is used mostly.
I have a navigation controller stack with different possible controllers in it.
I want to present a view controller modally as and when I need it based on the next view I am pushing in to navigation controller. (You can think of that as user login validation screen..which validates user before moving to next screen.)
I am able to do it successfully with
UIApplication.sharedApplication().keyWindow?.rootViewController?.presentViewController(contactPicker, animated: true, completion: nil)
and dismiss it when I need to dismiss with
UIApplication.sharedApplication().keyWindow?.rootViewController?.dismissViewControllerAnimated(false, completion: {
self.navigationController.pushViewController(myNewVC, animated: true)
})
I am able to do it however the transition from modally presented view to my (myNewvC) new push for next view is not exactly how i want it to be. When view dismiss happen I can see my caller screen for a second before i move to next view. I don't want to see that screen. Is it possible?
(Also I don't want to add validation screen in navigation stack as purpose of that screen is not to be part of navigation stack).
The completion block is exactly added here by Apple to protect you from uncaught exceptions because what you want to do might result in one, cause you can't do two animations at the same time, maybe some suggested solutions:
Try to present the new controller then when it's done hide the previous one in the completion block (this way when the user dismisses the new controller, he won't see the previous one), e.g.
Assuming you are in VC1 one now and you want to dismiss it and show VC2, you can use the below code in objective-c
[self presentViewController:VC2 animated:YES completion:^{
[self dismissViewControllerAnimated:YES completion:nil];
}];
In swift
self.presentViewController(VC2, animated: true) { () -> Void in
self.dismissViewControllerAnimated(true, completion: nil)
}
Or you can just ignore it if the user won't dismiss the new controller, then he would never see the old one but I don't think this is memory-efficient because the controller would be stuck in memory.
I didn't try it myself but if you use UINavigationViewController and you just don't want to go back that ViewController, than you can simple remove from stack after you go to the next ViewController.
In here you can see how you can change the UINavigationViewController's ViewController array:
Removing viewcontrollers from navigation stack
I have created a custom UIWebView to present a login page. I am able to get this to show by adding it as a subview. But I would like to present this modally and programatically without the use of a storyboard!
Put it in an otherwise empty view controller, and then call
[self presentViewController: animated: completion:]
in the view controller you're wanting to present it from. When you're done, you can call
[self dismissViewControllerAnimated: completion:]
to get rid of it again. This message can be sent to either the presenter or the presented view controller, though in either case it ultimately is the presenter which responds.
When my App is starting I have a modal view controller to enter credentials (IP#, username, password...). When the user is logged he can open many UIViewControllers that can open others UIViewController and so on... These view controller can be navigation controller, tab bars, modals....
Next, the user can leave the application in background. Next, user can open the Mail App and open a mail that contains an attachment to navigate to my App. When the user select my App to open the attachment.
When the App moves in foreground I need to go to a specific viewController of the hierarchy (the first ViewController opened after the login screen).
To move the App to this first ViewController I have tried to use
– dismissViewControllerAnimated:completion:
but without success, there're still a view from the hierarchy that is still displayed.
Any idea how to do this?
Regards,
Sebastien.
If all the view controllers are in 1 Navigation controller and pushed onto the stack, you can simply use either popToRootViewControllerAnimated: or popToViewController:animated:
Otherwise, why not dismiss the first Modal view controller, then put it back with the view controller you want.
I found a solution with this small piece of code
UINavigationController* navc = (UINavigationController*)viewController.topViewController;
if (navc != Nil) {
if (navc.presentedViewController != Nil) {
[navc dismissViewControllerAnimated:FALSE completion:Nil];
}
[navc popToRootViewControllerAnimated:TRUE];
}
It was not complex at the end!
Sébastien.
I have a view in which a user selects an action to take and on that next screen there is a save and a back button. For both of the buttons the last line is dismissViewControllerAnimated:.
I need a way to make the 1st screen show only if the back button is used. save should send back to the main screen/rootViewController I am fairly new to iOS but not programming in general and just need a nudge in the right direction.
Could I set a bool flag to show or not? Maybe I can set the Tag on the view and then check that in the other screens on save/back? I assume I can check the parent view.
Sorry if this is a dup but I cant find anything specifically for this.
EDIT: I am not using a nav controller and am showing the views modally.
The answer will vary depending on how your UIViewControllers are structured and setup. If you're using a uinavigationcontroller then you can POP to the root view controller using:
[self.navigationController popViewControllerAnimated:YES];
If you're presenting your UIViewControllers modally, you can try to dismiss the presenting View Controllers of your modal view controller using the presentingViewController property:
[[[self presentingViewController] presentingViewController] dismissViewControllerAnimated:YES completion:nil];
You may also want to take a look at Unwind Segues if you're using a Storyboard:
What are Unwind segues for and how do you use them?
Finally, as far as determining whether the back button is pressed or another button - that depends on how the app is setup. You'll need to use your own logic (probably if / then statements or case / switch) to determine which button was pressed. You also may want to check out the sender argument in IBActions.
John, to have a UINavigationViewController return to it's root viewcontroller, you use:
[nameOfNavController popToRootViewControllerAnimated:YES];
The other guys are correct that the information you've provided is definitely not enough to determine exactly what you need to do.
You can use the presentingViewController property of a modal view controller to access it's presenting controller.
It turns out that I was using the terminology wrong. I am presenting all views modally and that is the issue, there is no navigation controller. I ended up using NSNotification to build a listener and had the main view controller listen and then dismiss the view and hence show itself. Worked a treat.
here is the link to the code I ended up with.
http://iphonedevsdk.com/discussion/114737/view-heirarchy-issues-possibly-from-the-camera
Hopefully this helps someone else.