On apps using iOS 13, there is an option for ViewControllers to be shown as cards displayed on screen, or .automatic, that can be dismissed by a swipe. Adding a cancellation button, though, may dismiss the page, but, it does not include the animation of dismissing the card. Is there a way to add this programmatically?
This is my code, which is currently crashing the app:
#IBAction func cancel(_ sender: Any) {
let detailVC = home()
present(detailVC, animated: true)
}
If I just add a segue to a button, the animation is just the page popping over, not dismissing as if you were swiping. Thank you!
I think you want this:
#IBAction func cancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
What you're doing is preventing a new vc on an already presented modal.
What you actually want is to dismiss the currently presented modal.
See: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss
Related
In Start Developing iOS Apps (Swift): Implement Edit and Delete Behavior
, The offical tutorial tell me should use presentingViewController and navigationController to indicate the specified view is Edit or Show, Like following code:
#IBAction func cancel(_ sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
let isPresentingInAddMealMode = presentingViewController is UINavigationController
if isPresentingInAddMealMode {
dismiss(animated: true, completion: nil)
}
else if let owningNavigationController = navigationController{
owningNavigationController.popViewController(animated: true)
}
else {
fatalError("The MealViewController is not inside a navigation controller.")
}
}
The adding view is presented by modal, the editing view is presented by embed navigation controller, But I think this approach is not good to understand and easy maintain, How about introduce a isEditOrShow instance varible in the view to indicate the state? like following:
#IBAction func cancel(_ sender: UIBarButtonItem) {
// Depending on style of presentation (modal or push presentation), this view controller needs to be dismissed in two different ways.
if isEditingOrShow = .edit{
dismiss(animated: true, completion: nil)
}
else isEditingOrShow = .show{
owningNavigationController.popViewController(animated: true)
}
}
The tutorial explains that, there are two ways in which you can dismiss view controllers.
For example, when you present a view controller as modal, you can use the below code to dismiss it.
dismiss(animated: true, completion: nil)
However, if you are using a push presentation (Navigation controller), then you should use the below code to dismiss it.
owningNavigationController.popViewController(animated: true)
How about introduce a bool isEditOrShow instance varible in the view to indicate the state?
From what I understand, you won't be needing isEditOrShow variable. If you have any questions, let me know.
This question already has answers here:
Dismissing multiple modal view controllers at once?
(7 answers)
Closed 4 years ago.
I recreated the question and describe its essence more precisely.
I have two view controllers presented modally on Swift 4, without storyboard (can't use unwind) and without navigation controller
A presents B which presents C.
The reason why we don't use navigation controller, it's because we what simple animation from bottom to top and instead of breaking the standard animation from right to left, we decided to use present.
I would like to dismiss 2 view controllers and go from C to A.
Please, don't mark this question as duplicate before you read my question carefully. I found a tone of similar post, but neither solved my problem. Some of them Objective-C or some of the suggest to use:
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
Or:
self.presentingViewController?.dismiss(animated: false, completion: nil)
self.presentingViewController?.dismiss(animated: true, completion: nil)
It's works, but it create weird animation. It's just delete C and animate dismiss for B:
Expected result:
Idea: you need to dismiss third controller with animation and after dismissing you need to dismiss second without animation. While third controller is being dismissed, second shouldn't be visible.
First, set Presentation style of second view controller to Over Current Context when you're presenting it (since we will need to hide its view when we will dismiss third controller)
let vc2 = VC2()
vc2.modalPresentationStyle = .overCurrentContext
present(vc2, animated: true)
continue with creating callbacks properties for willDismiss and didDismiss inside third controller. This callback will be called before and after you dismiss third controller
class VC3: UIViewController {
var willDismiss: (() -> Void)?
var didDismiss: (() -> Void)?
#IBAction func dismissButtonPressed(_ sender: UIButton) {
willDismiss?()
dismiss(animated: true) {
self.didDismiss?()
}
}
}
then in second view controller in the place where you present third view controller, set third controller's callback properties: declare what happens when third controller will dismiss: you need to hide view of second and then after dismissing third you need to dismiss second without animation (view can stay hidden since view controller will be deinitialized)
class VC2: UIViewController {
#objc func buttonPressed(_ sender: UIButton) {
var vc3 = VC3()
vc3.willDismiss = {
self.view.isHidden = true
}
vc3.didDismiss = {
self.dismiss(animated: false)
}
present(vc3, animated: true)
}
}
Anyway, second option is using UINavigationController and then just call its method popToViewController(_:animated:).
Create a snapshot from the currently visible view and add it as a subview to the first presented view controller. To find that you can simply "loop through" the presenting view controllers and dismiss from the initial one:
#IBAction func dismissViewControllers(_ sender: UIButton) {
var initialPresentingViewController = self.presentingViewController
while let previousPresentingViewController = initialPresentingViewController?.presentingViewController {
initialPresentingViewController = previousPresentingViewController
}
if let snapshot = view.snapshotView(afterScreenUpdates: true) {
initialPresentingViewController?.presentedViewController?.view.addSubview(snapshot)
}
initialPresentingViewController?.dismiss(animated: true)
}
This is the result with slow animations enabled for the dismissal:
https://www.dropbox.com/s/tjkthftuo9kqhsg/result.mov?dl=0
If you are not using a navigation controller and you want to dismiss all ViewControllers to show the root ViewController (assuming A is the root):
self.view.window?.rootViewController?.dismiss(animated: true, completion: nil)
Use this in button action method.
The current VC will dismiss when you dismiss the parent VC.
This will dismiss both VCs in single animation.
self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
I have a Tab bar in the first view and a total of two views.
However, when I move from the first view to the second view and then back to the first view using the Segue, the Tab bar of the first view disappears.
When return to the first view from the second view, what is the way the Tab bar does not disappear?(without using unwind)
Don't use unwind segue here. When you need to get back to previous ViewController, just dismiss your current ViewController
dismiss(animated: true, completion: nil) /* call this in second VC */
I suggest you read the documentation for understanding how combined View Controller Interfaces. Anyway, if you need to pop to previous view controller on the flow, you need to use
navController.popViewController(animated: true)
But, if the need to pop up on the specific ViewController on the queue of View Controllers in the NavigationViewController, you need to use
navController.popToViewController(ViewController, animated: true)
From the moment you are using a NavigationController, a return button will automatically appear on the UINavigationBar, so you do not have to worry about that. Unless you want to customize the back buttons in the viewcontrollers queue, in this case use the above methods.
It's for page 1. Display tabBarController once page is loaded.
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.tabBar.hidden = true
}
If click event is fired, hide tabBarController.
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "twoSegue") {
self.tabBarController?.tabBar.hidden = false
}
}
In my swift app I have a UIViewController with a button. This button opens up the UIViewController number 2 and there user has another button. When user presses it - he opens UIViewController number 3. There is also a button and when user presses it - he calls the code:
self.dismissViewControllerAnimated(false, completion: nil)
and thanks to it the UIViewController number 3 disappears and user sees UIViewController number 2. My question is - is there a way of also dismissing the UIViewController number 2 so that user can come back smoothly from number 3 to number 1?
For now I created a function and call it through protocol:
UIViewController number 2:
protocol HandleYourFullRequest: class {
func hideContainer()
}
class FullRequest: UIViewController, HandleYourFullRequest{
func hideContainer(){
self.dismissViewControllerAnimated(false, completion: nil)
}
#IBAction func exitbuttonaction(sender: AnyObject) {
self.performSegueWithIdentifier("temporarySegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "temporarySegue"){
if let fullRequestDetails = segue.destinationViewController as? YourFullRequest
{
fullRequestDetails.delegateRequest = self
}
}
}
}
UIViewController number 3:
class YourFullRequest: UIViewController{
var delegateRequest:HandleYourFullRequest?
#IBAction func exitbuttonaction(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
delegateRequest?.hideContainer()
}
}
But with that solution when user presses the button - the UIViewController number 3 disappears and UIViewController number 2 appears for a second and disappears then. Is there a way of removing number 2 without showing it to the user and point him directly to the number 1?
I'm still unclear as two which button is wired to which action, but from what I can tell when the dismiss button is pressed on view controller 3 it calls self.dismissViewControllerAnimated(false, completion: nil) in view controller number 2.
Try putting this method in view controller 3.
#IBAction func exitButtonAction(sender: AnyObject) {
self.presentingViewController?.presentingViewController?.dismissViewControllerAnimated(true, completion: nil);
}
This will work assuming that both view controllers are presented and not pushed in something like a navigation controller.
You can use removeLast() function to pop controllers off the stack.
#IBAction func doneAction(sender: AnyObject) {
var vc = self.navigationController?.viewControllers
// remove controllers from the stack
vc?.removeLast()
vc?.removeLast()
// Jump back to the controller you want.
self.navigationController?.setViewControllers(vc!, animated: false)
}
You can use popToViewController(viewController: UIViewController, animated: Bool)
Where viewController is the viewController you wish to pop to, in your case, 'UIViewController number 1'.
popToViewController Documentation
If you don't have a reference to the View Controller, you can get it from self.navigationController.viewControllers, it will be the first object in your example.
There is a method on UINavigationController called setViewControllers. It takes an array of all the view controllers you want active. You can get all the view controllers on the stack as an array, remove the ones you don't want, then call setViewControllers with the updated array.
I have a navigation tableview controller Calculator and that goes to AddActiveIngredients which is where I add everything in for the calculator.
I have the segue method backButton in Calculator and in AddActiveIngredients I have click dragged the segue from the controller to exit and selected the segue in the Calculator class.
The segue identifier is also backButton but it's not doing anything.
I've tried this code to try and trigger the segue manually but that's not working. Am I missing something so simple?
override func didMoveToParentViewController(parent: UIViewController?) {
if (!(parent?.isEqual(self.parentViewController) ?? false)) {
print("Back Button Pressed!")
self.performSegueWithIdentifier("backButton", sender: self)
}
}
It sounds like you just want to remove the AddActiveIngredients from the stack. If so, you just need to call a function to dismiss it.
func dismissVC() {
dismissViewControllerAnimated(true, completion: nil)
}
You can put that in a button tap or something else. It'll work with Show and Modal segues. Unwinding may be overkill for what you're doing.
Hope it helps!
To use the Exit icon you need not create a segue at all.
All you have to do is add a method in the ViewController to which you want to unwind to and add a method with the signature:
#IBAction func myUnwindAction(segue: UIStoryboardSegue) {
// do stuff
}
Remember, you have to add this method in the target ViewController
When you Control-Drag from a button to the Exit icon, this method will now show up.
When you now click the button, the current ViewController will be popped and the action method in the target will be called.
Hope this helps.
with Swift 5 you can use Dismiss function
#IBAction func dismissViewController(_ sender: Any) {
//Go back to previous controller
dismiss(animated: true, completion: nil)
}