Unwind segue from navigation back button in Swift - ios

I have a settings screen, in that I have a table cell. By clicking on that I take to another screen where user can choose an option and I want it back in the previous view controller when back button is pressed.
I can put a custom bar button item, but I want to return to the parent view controller using the back button in the navigation bar rather than with a custom button on the view.
I don't seem to be able to override the navigation back button to point it down to my unwind segue action and since the back button doesn't appear on the storyboard, I cant drag the green Exit button to it
Is it possible to unwind a push segue with the back button?

Here's my solution, based on Objective-C code from Blankarsch to this StackOverflow question: How to trap the back button event
Put this code inside the View Controller you want to trap the Back button call from:
override func didMoveToParentViewController(parent: UIViewController?) {
if (!(parent?.isEqual(self.parentViewController) ?? false)) {
println("Back Button Pressed!")
}
}
Inside of the if block, handle whatever you need to pass back. You'll also need to have a reference back to calling view controller as at this point most likely both parent and self.parentViewController are nil, so you can't navigate the View Controller tree.
Also, you might be able to get away with simply checking parent for nil as I haven't found a case where pressing the back button didn't result in parent being nil. So something like this is a bit more concise:
override func didMoveToParentViewController(parent: UIViewController?) {
if (parent == nil) {
println("Back Button Pressed!")
}
}
But I can't guarantee that will work every time.

Do the following in the view controller that has the back button
Swift 3
override func didMove(toParentViewController parent: UIViewController?) {
if !(parent?.isEqual(self.parent) ?? false) {
print("Parent view loaded")
}
super.didMove(toParentViewController: parent)
}

I tried the same and it seems that you cannot catch the standard back button's action so the solution will be to use a custom button and bind it to a segue which leads back to the previous page.

You could use some sort of delegation as you did or use a custom back button and an unwind segue.
Better even, you could handle passing data between your view controllers using a mediator:
http://cocoapatterns.com/passing-data-between-view-controllers/

Related

How to segue from tab bar view controller to another view which is a child of a navigation controller

I laid out my home screen (Summary screen) as shown on the screenshot below:
I added the "+" button on the UITabBarController subclass. This button will segue to a view controller and show it modally. To give you a bit of context on how I've structured my storyboards, please refer to the screenshot below:
This modal view controller has a form that the user needs to fill in, and once they're done, there's a 'Done' button which will 1) dismiss the modal, 2) take them back to the root view which is either the 'Summary' tab or the 'Details' tab then 3) take them to a list view showing the most recent data they've entered on a list (destination view – highlighted in red).
Now, doing the segue when the user taps the "+" button is simple enough. With the following code:"
menuButton.addTarget(self, action: #selector(menuButtonAction(sender:)), for: .touchUpInside)
then
func addNewExpense(action: UIAlertAction){
self.performSegue(withIdentifier: "segue_formModal", sender: self)
}
On the modal I've setup a protocol which will then send the form data back to the tab view controller.
extension XtabBarViewController: NewInfoDelegate{
func newInfoSubmitted(formData: FormData) {
performSegue(withIdentifier: "segue_ListingPage", sender: self)
}
}
But it seems that the perform segue is not working. It says the segue does not exist. "segue_ListingPage" is that segue that connects the Summary view and the destination view.
How can I segue from the tab bar view controller to the destination view? Any help is appreciated.
Thanks in advance!
You can just make the navigation view controller the entry point.
I figured it out on my own. For the benefit of those who run into the same issue here's my solution.
I simply added the code from the tab bar view controller the code:
let childVC = children[0].children[0]
childVC.performSegue(withIdentifier: "segue_TXListPage", sender: self)
So what I did here is call the performSegue from the view controller that segues to the destination controller.

how to unwind segue to the view controller that called it

Below is a great answer of how to use Apple's unwind segue.
What are Unwind segues for and how do you use them?
My problem with the answer however is that you have to explicitly tell storyboard which view controller you want to exit (unwind) back to. What I'm trying to do is exit (unwind) back to whichever view controller called it, only using one button.
Let's say I have 3 view controllers: Red, Blue, and Yellow.
Both Red and Blue have a button on them to go to the Yellow view controller, but Yellow only has one button, return. Is it possible to have the yellow return button unwind back to whichever view controller called it?
thanks to #luk2302 I was able to figure it out. No need to even use unwind segue. Thanks luk2302!
#IBAction func returnViewController(sender: AnyObject) {
if((self.presentingViewController) != nil){
self.dismissViewControllerAnimated(true, completion: nil)
}
}

navigating through desired views (while skipping some views) using UINavigationBar back button

I am using a UICollectionView to show a list of images.
1)By clicking on any cell the image and some description about it is displayed in the resulting view controller(using a push segue).
2)When i swipe from the left/right edge(using PanGesture) i need to display the details of the previous/next image in the collection view.
3)But the back button of the navigation bar has to take me back to the collection view and not to the previous displayed details (shown by the PanGesture).
I know how to get 1 and 2 done but don't have a concrete idea to get the 3rd job done.
Any help would be appreciated.
You can find your desired UIViewController in your navigation stack using for loop. Try this. This is in Swift
for (var i = 0; i < self.navigationController?.viewControllers.count; i++) {
if (self.navigationController?.viewControllers[i].isKindOfClass(YourViewController) == true) {
println("is sw \(self.navigationController!.viewControllers[i])")
(self.navigationController!.viewControllers[i] as! YourViewController)
self.navigationController?.popToViewController(self.navigationController!.viewControllers[i] as! YourViewController, animated: true)
break;
}
When you navigate from one view to another (e.g. by showing a detail view of your images one after the other, then all these views are internally piled up as a stack.
Thus, if you want to jump directly to a view somewhere in between this stack (e.g. the collection view), then you can use the Unwind Segue.
In your case it should work like this:
First, in your collection view (i.e. your back button destination) you need to implement a UIStoryboard Segue as follows
#IBAction func myGoBackPoint(segue: UIStoryboardSegue) {
println("Jump directly back here from any other view")
}
Then, in the storyboard of your detail view you ctrl-drag directly to top rightmost Exit marker and choose the previously created back button destination:
In the code of the detail view implement the "go back instruction"
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "My Unwind Segue" {
if let myUnwindSegue = segue.destinationViewController as? MyCollectionViewController {
// prepare for segue
}
}
}
Hope this helps.
Finally after researching a lot i found that using a collection view with each cell taking up the whole screen is the best way to go about solving this issue.
Thankyou

Swift: Buggy Navigation Bar Behavior With popViewControllerAnimated

I want to use a push segue to edit an "entry" that is otherwise added via a present modally segue. It doesn't dismiss using the normal dismissViewControllerAnimated method when pressing cancel. Because of this I had to combine the popViewControllerAnimated method at the same time, so that depending on whether they click cancel when editing an entry or adding it, it will try both.
Both are done via NSNotifcation, because of objects I need to carry back from the last viewcontroller to the first:
func cancel(notification: NSNotification){
println("Cancel Executed")
let userInfo:Dictionary<String,EntryItem!> = notification.userInfo as Dictionary<String,EntryItem!>
entry = userInfo["Object"]
tableView.reloadData()
self.navigationController?.popViewControllerAnimated(true)
dismissViewControllerAnimated(true, completion: nil)
dataModel.saveEntries()
}
The problem with this is that if I go through the segues to arrive at the third view controller (in a string of 5), I cancel, and it goes back to the entries screen, but a messed up looking navigation bar takes the place of what is supposed to be there. There's no title showing either. It has a cancel button which causes a crash if you press it.
Here's what it's supposed to look like:
Here's what the popViewControllerAnimated does to it.

Swift: Perform a function on ViewController after dismissing modal

A user is in a view controller which calls a modal. When self.dismissViewController is called on the modal, a function needs to be run on the initial view controller. This function also requires a variable passed from the modal.
This modal can be displayed from a number of view controllers, so the function cannot be directly called in a viewDidDisappear on the modal view.
How can this be accomplished in swift?
How about delegate?
Or you can make a ViewController like this:
typealias Action = (x: AnyObject) -> () // replace AnyObject to what you need
class ViewController: UIViewController {
func modalAction() -> Action {
return { [unowned self] x in
// the x is what you want to passed by the modal viewcontroller
// now you got it
}
}
}
And in modal:
class ModalViewController: UIViewController {
var callbackAction: Action?
override func viewDidDisappear(_ animated: Bool) {
let x = … // the x is what you pass to ViewController
callbackAction?(x)
}
}
Of course, when you show ModalViewController need to set callbackAction like this modal.callbackAction = modalAction() in ViewController
The answer supplied and chosen by the question asker (Michael Voccola) didn't work for me, so I wanted to supply another answer option. His answer didn't work for me because viewDidAppear does not appear to run when I dismiss the modal view.
I have a table and a modal VC that appears and takes some table input. I had no trouble sending the initial VC the modal's new variable info. However, I was having trouble getting the table to automatically run a tableView.reloadData function upon dismissing the modal view.
The answer that worked for me was in the comments above:
You likely want to do this using an unwind segue on the modal, that
way you can set up a function on the parent that gets called when it
unwinds. stackoverflow.com/questions/12561735/… – porglezomp Dec 15
'14 at 3:41
And if you're only unwinding one step (VC2 to VC1), you only need a snippet of the given answer:
Step 1: Insert method in VC1 code
When you perform an unwind segue, you need to specify an action, which
is an action method of the view controller you want to unwind to:
#IBAction func unwindToThisViewController(segue: UIStoryboardSegue) {
//Insert function to be run upon dismiss of VC2
}
Step 2: In storyboard, in the presented VC2, drag from the button to the exit icon and select "unwindToThisViewController"
After the action method has been added, you can define the unwind
segue in the storyboard by control-dragging to the Exit icon.
And that's it. Those two steps worked for me. Now when my modal view is dismissed, my table updates. Just figured I'd add this, in case anyone else's issue wasn't solved by the chosen answer.
I was able to achieve the desired result by setting a Global Variable as a boolean value from the modal view controller. The variable is initiated and made available from a struct in a separate class.
When the modal is dismissed, the viewDidAppear method on the initial view controller responds accordingly to the value of the global variable and, if needed, flips the value on the global variable.
I am not sure if this is the most efficient way from a performance perspective, but it works perfectly in my scenario.

Resources