Perform action when segue is closed - ios

I have a ViewController where I call segue to another ViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == blogSegueIdentifier {
let destination = segue.destination as! Reply
let path = tableView.indexPathForSelectedRow
let cell = tableView.cellForRow(at: path!) as! QuestionsTabCell
destination.blogName = String(cell.labelQues!.tag)
}
}
In the opened segue (ViewController) there is a UITextView and a send button in the UINavigationBar. So user inputs textView and sends it to server tapping on the button and after this opened segue (ViewController) closes and user returns to first ViewController. How to call a method in first ViewController when user closes segue without using viewWillAppear?

Hello #Mr Jo You can use unwind segue for this purpose.
declare a method in your first viewController Like :-
//MARK: - - Unwind Segue get data from select categories
#IBAction func unwindToAssignCatController(segue: UIStoryboardSegue) {
//get back data from selectcategory class
if segue.identifier == "segueIdentifier"{
let controller = segue.source as! Reply
// get your values from second viewController and use in first viewController
}
After that you have to select your second ViewController
1: right Click on Controller and drag on exit then below method will appear
2: Select it and assign an identifier to it.
3: then perform segue on that action where you want in second viewController with same identifier.

If you have the NavigationController, try: (Objective-C)
FirstVC* firstVC = self.navigationController.viewControllers[self.navigationController.viewControllers.count
- 2]
last but one in a series of viewControllers in the navigation stack
If you are opening the second viewController without the NavigationController, you can get the instance of the first viewController like:
FirstVC* firstVC = self.presentingViewController
Please not that the instance of the presenting VC will be available in this property after the transition will finish, in the viewDidAppear, for example

Related

Cannot update embedded child VC from non-parent VC

Up to date Xcode/Swift/iOS.
I have a Master VC (called StartVC) that contains a Child VC (called TopBarVC) via and embedded segue. The Child VC contains a button, that, when pressed, modally segues to a 3rd VC (called CategoryPickerOverlayVC) (the view in this VC serves as a dropdown box for picking a category).
#IBAction func CategoryFilterButtonPressed(_ sender: Any) {
performSegue(withIdentifier: "toCategoryPickerOverlay", sender: self)
}
When an option is selected from the dropdown box, which itself is composed of three buttons, the title of the selected button should be used to replace the title text of the button in the Child VC.
In the Master VC, I use prepareforsegue to store a reference to the Child VC in a variable - "topBarReference" - at the moment when the embed segue takes place.
var topBarReference: TopBarVC?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "TopBarPane"{
topBarReference = segue.destination as? TopBarVC
}
}
Then, in the 3rd VC, when I click on one of the button options in the dropdown box, the button title is sent via a prepareforsegue to update the button in the Child VC (via "topBarReference").
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.filterButtonText = ((sender as! UIButton).titleLabel?.text)!
}
The 3rd VC then unwind segues back to the Master VC. I should add that when the button in the Child VC is changed, a variable (filterButtonText) in Child VC is first set with the title text and then this variable is then used to set the button title text via the viewDidAppear method of Child VC.
When using the debugger, I also note that viewDidAppear in the Master VC does not seem to execute after unwinding (I placed a diagnostic print-to-console in viewDidAppear and nothing prints after the unwind segue). I realise this would explain the button not getting updated but I've got no idea why viewDidAppear does not run.
I have also tried using a delegate protocol and instantiateViewController(withString:) to no avail. All of the methods produce the same result, which is that the button in the Child VC does not get updated. No errors are shown. Everything else happens as expected.
Any ideas as to what I am doing wrong?
Do you mean something like this?
If so, the solution I used was very simple: the third VC uses prepareForSegue to set a property of the embedded VC, and the embedded VC picks up that property in the unwind method.
In my implementation, the three view controllers are called ViewController, ChildViewController, and ThirdViewController. This is the entire code (everything else is configured in the storyboard):
class ChildViewController: UIViewController {
#IBOutlet weak var theButton: UIButton!
var buttonTitle : String?
#IBAction func unwind(_:UIStoryboardSegue) {
self.theButton.setTitle(self.buttonTitle, for: .normal)
}
}
class ThirdViewController: UIViewController {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
(segue.destination as! ChildViewController).buttonTitle = (sender as! UIButton).currentTitle
}
}
Ok, so I have found that my original code works fine bar one line in the prepareforsegue of the Child VC. If I change that prepareforsegue from:
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.CategoryFilterButton.titleLabel?.text = ((sender as! UIButton).titleLabel?.text)!
}
to this:
if segue.identifier == "unwindToStartVC"{
let vc = segue.destination as! StartVC
vc.topBarReference?.CategoryFilterButton.setTitle((sender as! UIButton).titleLabel?.text, for: .normal)
}
it works just fine. The use of the .setTitle method seems to make a difference although I am not sure why.
Thanks to Matt for giving me the idea to change it to that. Matt's method did work when i tried it, although, as I am unwinding to the Master VC and not the Child VC, I had to edit the code accordingly, in terms of where I placed it.
As my little "discovery" equates to the smallest change to the original code, I'll mark this as the answer.
Thanks to all for taking the time to respond!

How to pass values from a Pop Up View Controller to the Previous View Controller?

So In my 1stViewController I have this code:
#IBAction func colorDropdown(_ sender: Any) {
self.popUpColorPicker()
}
func popUpColorPicker() {
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ColorPicker") as! ColorPicker
self.addChildViewController(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParentViewController: self)
}
Which would pop up the 2ndViewController. Upon dismissing the Pop Up 2ndViewController, I'd like to retrieve the values I entered and use it in my 1st View Controller.
You can achieve this by either using delegate or completion handler.
Just create a delegate to handle your data on dismissing the second VC.
**
OR
**
Write a completion handler closure to get back those values in your first view controller.
Suppose A & B are two controllers and you first navigated from A to B with some data. And now you want to POP from B to A with some data.
Unwind Segues is the best and recommended way to do this.
Here are the steps.
Open A.m
define following method
#IBAction func unwindSegueFromBtoA(segue: UIStoryNoardSegue) {
}
open storyboard
Select B ViewController and click on ViewController outlet. press control key and drag to 'Exit' outlet and leave mouse here. In below image, selected icon is ViewController outlet and the last one with Exit sign is Exit Outlet.
You will see 'unwindSegueFromBtoA' method in a popup . Select this method .
Now you will see a segue in your view controler hierarchy in left side. You will see your created segue near StoryBoard Entry Piont in following Image.
Select this and set an identifier to it. (suggest to set the same name as method - unwindSegueFromBtoA)
Open B.m . Now, wherever you want to pop to A. use
self.performSegueWithIdentifier("unwindSegueFromBtoA", sender: dataToSend)
Now when you will pop to 'A', 'unwindSegueFromBtoA' method will be called. In unwindSegueFromBtoA of 'A' you can access any object of 'B'.
That's it..!
you can always use unwind to do some stuff upon unwinding
declare a IBAction in your first vc
var color: UIColor!
#IBAction func unwindToCheckout(segue: UIStoryboardSegue) {
//do some stuff with color
}
then create exit segue for popout viewcontroller
then you can dismiss popout like this
self.performSegueWithIdentifier("unwindToVC1", sender: selectedColor)
then in prepareForSegue
if segue.identifier == "unwindToVC1" {
(segue.destinationViewController as! FirstViewController).color = sender as! UIColor
}
also you can create delegate to reach fistviewcontroller and do some stuff which is way easier to do

Swift - Unwind Segue triggering error

I am using for the first time the Unwind Segue approach. I have multiple view controllers as can be seen in the picture below (a few of them shown of course). In the Edit Profile I have a Done button. Once clicked I trigger an IBAction that triggers an unwind segue.
Here is the code for the Done button in the nav bar:
#IBAction func unwindToMainViews(sender: UIStoryboardSegue) {
//let sourceViewController = sender.sourceViewController
self.performSegueWithIdentifier("unwindToMainSegue", sender: self)
}
and in the
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!)
I am doing the following to pass data from Edit Profile back to Home View
if (segue.identifier == "unwindToMainSegue") {
// pass data to next view
let tabCtrl = segue.destinationViewController as! UITabBarController
let destinationVC = tabCtrl.viewControllers![0] as! HomeViewController
destinationVC.userObject = self.userObject;
}
When segue identifier is matched and code is executed (to transfer userObject from this controller to another), it triggers the error:
Could not cast value of type 'app.EditProfileViewController' (0x100b99d80) to 'UITabBarController' (0x104a1d030).
How can this error be fixed? I am surprised since i am casting to UITabBarController so thought it should work.
You don't return to the UITabBarController in an unwind segue. You return to the ViewController that triggered the original segue, or one if its ancestors.
As #jlehr stated in the comments:
Unwind segues don't return to anything; they dismiss any pushed and
presented view controllers between the source and destination view
controller. The destination is wherever the implementation of the
unwind method is found, regardless of how the source view controller
was presented.
To unwind to the viewController that triggered the original segue, you need to implement the #IBAction func returnToHere(segue: UIStoryboardSegue) function in the viewController you want to return to.
Then when you set up your Done button by dragging to the Exit icon, you select returnToHere from the pop-up.
To pass data back to the sourceViewController, give your unwind segue an identifier such as "unwindToSource", then override prepareForSegue in the viewController you are returning from:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "unwindToSource" {
let dvc = segue.destinationViewController as! SourceViewController
dvc.userObject = self.userObject
}
}

Passing information between UICollectionViewControllers through unwind segues

I have two UICollectionViewControllers and the first one uses a push segue to get to the second one. The problem I'm having is passing information back to the first controller when the back button (the one that gets added automagically) is pressed in the second controller. I've tried using the segueForUnwindingToViewController, and canPerformUnwindSegueAction override functions, but no dice. I need to be able to access both view controllers so I can set some variables. Any ideas?
Here is an example with two view controllers. Let's say that the names of the two view controllers and ViewController and SecondViewController. Let's also say that there is an unwind segue from the SecondViewController to the ViewController. We will pass data from the SecondViewController to the ViewController. First, let's set the identifier of this segue by opening the document outline and selecting the unwind segue. Then open up the attributes inspector and set the identifier to "unwind".
SecondViewController Code:
class SecondViewController: UIViewController
{
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
if let identifier = segue.identifier {
if let destination = segue.destinationViewController as? ViewController {
if identifier == "unwind" {
destination.string = "We Just Passed Data"
}
}
}
}
}
ViewController Code:
class ViewController: UIViewController {
var string = "The String That Will Be We Just Passed Data"
#IBAction func unwindSegue(segue: UIStoryBoardSegue) {
}
}
It sounds like you are trying to intercept the back button, there are many posts for this on SO, here are two:
Setting action for back button in navigation controller
Trying to handle "back" navigation button action in iOS
In practice, it is more clear to return state in closures (more modern), or delegates.

Swift: Segue(modal) to a UITableViewController which embeded in antoher navigation controller

Background: I want to display a modal segue from a UITableViewController(A) to a UITableViewController(B), but I want to show a NavigationBar to "Cancel" and "Save".
What I've done:
In storyboard:
I ctrl drag the cell from A to B, and set segue identifer "selectItem"
I choose B and select "Editor - Embed in - Navigation Controller"
In A's ViewController:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifer == "selectItem" {
if let indexPath = self.tableView.indexPathForSelectedRow() {
let destinationViewController = segue.destinationViewController as B
// Pass value from A to B
}
}
}
Error:The app crashed at let destinationViewController = segue.destinationViewController as B with the error swift_dynamicCastClassUnconditional. If I didn't embed a navigation controller to B, the program would not crash. But I really need a navigation bar.
Is there any solution or other way to achieve this? Thank you!
PS: I tried drag a NavigationBar from object library in storyboard, but it's miss a part of background to cover statusbar...
Create both of the UITableViewController in your storyboard.
Select your Second UITableViewController (the one you want to present modally), and embed it in an UINavigationController.
Add the "Cancel" and "Save" UIBarButtonItem into UINavitionItem of the Second UITableViewController.
Select the UITablViewCell of your First UITableViewController
Control+Drag into your UINavigationController.
Select "Present Modally" under the "Selecteion Segue" option from the dropdown list.
To pass data in your First UITableViewController override the method:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "identifier" {
let destination = segue.destinationViewController as UINavigationController
let bViewController = destination.topViewController as BViewController
// pass data
}
}
Here are the screenshots:
This should do the job, Cheers!
I solved it!
I added a BreakPoint at the line let dest = segue.destinationViewController as B, and I found that segue.destinationViewController is NavigationController type. So I fixed it by replacing this line to:
let dest = segue.destinationViewController as UINavigationController
let bVC = dest.topViewController as B
and do some passing value stuff.
Hope this will help other people facing this problem.
I use
if let b = segue.destinationViewController.childViewControllers.first as? B {
//do something with b
}
for this scenario. It is really just a syntax difference though.

Resources