I have 3 VC - VC1, VC2 & VC3
I'm creating an unwind segue where -
VC1 is destination
VC2 is source
So, I've add Marker function in VC1 -
#IBAction func unwindToHomeViewController(_ sender: UIStoryboardSegue) {}
and in VC2 I've created two variable -
var userSelectedPlacesLatitude: Double = 0
var userSelectedPlacesLongitude: Double = 0
which will be updated in tableView -
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.userSelectedPlacesLatitude = suggestedPlacenames[indexPath.row].geometry.coordinates[1]
self.userSelectedPlacesLongitude = suggestedPlacenames[indexPath.row].geometry.coordinates[0]
print("In didSelectRowAt", userSelectedPlacesLatitude, userSelectedPlacesLongitude)
}
and then prepare for segue -
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let destinationVC = segue.destination as! VC1
print("In Segue preperation",userSelectedPlacesLatitude, userSelectedPlacesLongitude)
destinationVC.userSelectedPlacesLatitude = self.userSelectedPlacesLatitude
destinationVC.userSelectedPlacesLongitude = self.userSelectedPlacesLongitude
destinationVC.reloadWeatherDataStatus = true
}
But from print value I'm seeing that prepareforSegue is called earlier than didSelectRowAt. Hence I'm not getting expected value in prepareforsugue
In Segue preperation 0.0 0.0
In didSelectRowAt 49.3227937844972 31.3202829593814
Hence 0.0 0.0 is passing all the time to VC1. How can I fix this problem?
The problem you are experiencing results from having at the unwind segue linked directly from the table view cell in your storyboard. As soon as the user taps the row, the unwind segue fires. The didSelectRow(at:) function is called after, but this is too late; You are already back in VC1.
While you can use prepareForSegue to send data to VC1, a better approach is to use the sender passed to unwindToHomeViewController to let VC1 get the data from VC2.
This means that VC2 doesn't need to know anything about VC1. You can also get rid of the reloadWeatherDataStatus property and simply reload the weather data status in the unwind function.
You should:
Remove the segue from the table view row in VC2
In your storyboard, ctrl-drag from the "View controller" icon at the top of VC2 to the "Exit" icon at the top of VC2 and select unwindToHomeViewController
Select the newly created unwind segue and give it an identifier, say unwindToVC1
In VC2 you have
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.userSelectedPlacesLatitude = suggestedPlacenames[indexPath.row].geometry.coordinates[1]
self.userSelectedPlacesLongitude = suggestedPlacenames[indexPath.row].geometry.coordinates[0]
self.performSegue(withIdentifier:"unwindToVC1", sender: self)
}
Remove prepare(for segue: sender:) from VC2
In VC1
#IBAction func unwindToHomeViewController(_ sender: UIStoryboardSegue) {
if let sourceVC = sender.source as? VC2 {
self.userSelectedPlacesLatitude = sourceVC.userSelectedPlacesLatitude
self.userSelectedPlacesLongitude = sourceVC.userSelectedPlacesLongitude
// Do whatever is required to reload the data based on the new location
}
}
Try the code below and let me know if it works -
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let destinationVC = VC1()
destinationVC.userSelectedPlacesLatitude = suggestedPlacenames[indexPath.row].geometry.coordinates[1]
destinationVC.userSelectedPlacesLongitude = suggestedPlacenames[indexPath.row].geometry.coordinates[0]
destinationVC.reloadWeatherDataStatus = true
destinationVC.performSegueWithIdentifier("DestinationSegueName", sender: self)
}
Adding modifications to this answer since some people might have problems with creating the VC instance -
Step 1 - Create a manual segue named "SegueToDestinationVc" from source(VC1) to destination(VC2) view controller and write this code in source view controller -
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "SegueToDestinationVc") {
let vc = segue.destination as! VC2
vc.dataToPass = someData
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
someData = placeName[indexPath.row]
}
Step 2 - In destination view controller(VC2) has a public property named "dataToPass" and use it.
Happy to help, Thanks.
Happy Coding
Let me know if you need any other help.
Related
I have a Navigation controller embedded to a VC called UserDashboardVC. I then have a menu VC and the one option opens another VC with a tableview, ManageAccountVC. When I select the table row I would like it to unwind and populate the data on the UserDashboardVC.
I'm struggling to pass the data back to the UserDashboardVC using unwind segue from ManageAccountVC.
In my UserDashboardVC (root) I have my unwind segue code:
#IBAction func unwindUserDashboardVC(_ unwindSegue: UIStoryboardSegue) {
userCompanyLabel.text = PassCompanyOffice}
In my ManageAccountVC the tableview cell has been connected to Exit unwindUserDashboardVC, this works, when I click on the cell I unwind to UserDashboardVC. I have a function in ManageAccountVC to get the row selected:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
PassCompanyOffice = userAccountArray[indexPath.row].companyOffice!
}
I also have the Prepare function in ManageAccountVC which seems to trigger before I get my row value:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let ConfirmVC = segue.destination as! UserDashboardVC
ConfirmVC.PassCompanyOffice = PassCompanyOffice
}
Why is my unwind segue being performed before my cell row is identified? How do I pass back my data?
You may want to create a class member to hold the selected row and assign that in willSelectRow for the tableview. Then pickup the value in the prepare for Segue or the unwind segue in this case. Put a breakpoint in the unwind segue to determine the state of the selected row variable before using it.
Manual Segue
To separate the cell click action from the segue you will need to remove the exit segue that you made from the tableview cell to the exit.
Then create a manual segue from the view controller to the exit icon. Give that segue an identifier and then call the performSegue with identifier (using the exitSegueIdentifier)
This way you separate the two actions. You can click on the table without exiting. In your code base you can decide when you want to call the performSegue and actually close the VC using the manual exit segue.
As #Tommie C. mentioned, my issue was with the TableViewCell that was linked to the Storyboard Exit. I removed that segue and added a manual Exit seguel by doing this:
Make sure the above is made identifiable, in this case it is "loadDashboardSegue". The Prepare function has the segue identifier name "loadDashboardSegue":
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "loadDashboardSegue" {
let ConfirmVC = segue.destination as! UserDashboardVC
ConfirmVC.PassCompanyOffice = PassCompanyOffice
}
}
Lastly, you action the segue manually in the tableview row select:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
PassCompanyOffice = userAccountArray[indexPath.row].companyOffice
performSegue(withIdentifier:"loadDashboardSegue", sender: self)
}
This question already has an answer here:
Passing data from tableView to ViewController in Swift
(1 answer)
Closed 5 years ago.
I have a tableview and I want to show cell details in another view controller.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let selectedCell = listAthkar[indexPath.row]
let destinationVC = showCellVC()
destinationVC.cellTitle.text = selectedCell.title
destinationVC.cellDisc.text = selectedCell.details
performSegue(withIdentifier: "showCell", sender: self)
}
showCellVC has a UILabel and a textview which I want to pass data to, the data are coming from core data.
The app crashes every time I press in a cell.
Here is the error I get
fatal error: unexpectedly found nil while unwrapping an Optional value
2017-08-27 02:46:29.315056-0400 AthkarKF[13152:3972483] fatal error:
unexpectedly found nil while unwrapping an Optional value
The error I think is self-explanatory, but I'm not sure where is the optional value and I'm not sure if this is the correct way to pass data to another VC.
Can you please help, I would really appreciate it.
What you should do is to pass the desired data through prepareForSegue:sender: method. You could achive this by doing the following:
1- Declare selectedCell as an instance variable to be accessible in the whole view controller:
// for sure you'll need to declare its data type...
var selectedCell:...
2- Remove "passing data" code from tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) delegate method, all you have to do is to perform the segue:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedCell = listAthkar[indexPath.row]
performSegue(withIdentifier: "showCell", sender: self)
}
3- Implement prepareForSegue:sender: and pass the data through it:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// you may need to check the segue identifier in case of your view controller contains multiple segues
if segue.identifier == "showCell" {
let destinationVC = segue.destination as! showCellVC()
destinationVC.cellTitle.text = selectedCell.title
destinationVC.cellDisc.text = selectedCell.details
}
}
ّIn general, the final result should be similar to:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
// STEP #01:
// for sure you'll need to declare its data type...
var selectedCell:...
// STEP #02:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
selectedCell = listAthkar[indexPath.row]
performSegue(withIdentifier: "showCell", sender: self)
}
// STEP #03:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// you may need to check the segue identifier in case of your view controller contains multiple segues
if segue.identifier == "showCell" {
let destinationVC = segue.destination as! showCellVC()
destinationVC.cellTitle.text = selectedCell.title
destinationVC.cellDisc.text = selectedCell.details
}
}
}
I have a UIViewController which should show me DetailInformations depending on what Cell was pressed in the UITableViewController.
For the moment I am passing them through a sequel:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "show" {
var ctrl = segue.destination as! DetailViewController
ctrl.information = _informationList[id]
}
}
The id variable is set through:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
id = indexPath.row
}
Now in my UIViewController I change the information with:
override func viewDidLoad() {
super.viewDidLoad()
setInformation(i: information)
}
Now my problem is, that if I press, lets say cell 2. It switches to the ViewController and shows Information of cell 1. Than I go back to the tableview and I press cell 3. Then it shows me cell 2.
In short, it seems that the viewController is loaded (with the last information), before it sets the new information.
Is there any better way to solve this?
Try using indexPathForSelectedRow in prepareForSegue as of it looks like that you have created segue from UITableViewCell to the Destination ViewController so that prepareForSegue will call before the didSelectRowAt.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "show" {
var ctrl = segue.destination as! DetailViewController
if let indexPath = self.tableView.indexPathForSelectedRow {
ctrl.information = _informationList[indexPath.row]
}
}
}
I am assuming based on what you are describing is that you used a segue in your Storyboard to link directly from the cell to the detail view controller. This is not what you want to do, as mentioned earlier, because you don't get the order of events you would expect. You could use the delegation design pattern for this, but assuming you want to stick with segues you need to make the "show" segue from the table VC itself to the detail VC. You then manually call the segue from the tableView didSelectRowAt code.
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
id = indexPath.row
performSegue(withIdentifier: "show", sender: self)
}
Finally, you could then use an unwind segue when you come back to catch any data changes initiated in the detail VC.
This question relates to:
SWIFT: Push segue is resulting in a modal segue instead
'Show' segue in Xcode 6 presents the viewcontroller as a modal in iOS 7
I understand this question might be very similar to others. But I have been unable to use some of the answers to solve my issue.
Here is how my storyboard looks:
The viewController has a segmentControl that controls two viewControllers. I now want to segue to the DetailViewController, but it is appearing as modal segue which hides the tabBar and navigationBar.
I have tried deleting and recreating the segue as the some off the answers have suggested but it doesn't solve anything. Is there anything someone could suggest me or direct me to?
Edit:
After testing out the demo that the pod provides I was able to outline the issue I am struggling with. I have implemented the same methods in which it is practically identical. The only difference is that my method for this PageMenu does not use nib files like the demo has done.
In my tableView delegate I am trying to pass a recipe data to the DetailView. This is how my prepareForSegue and didSelect looks:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "detail", sender: recipe)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detail" {
let vc = segue.destination as! DetailViewController
let indexPath = tableView.indexPathForSelectedRow!
vc.recipe = RecipeManager.shared.recipes[indexPath.row]
}
}
Here is the demo's didSelect:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let newVC : UIViewController = UIViewController()
newVC.view.backgroundColor = UIColor.white
newVC.title = "Favorites"
parentNavigationController!.pushViewController(newVC, animated: true)
}
When comparing it with the demo I am struggling to understand where to implement the parentNavigationController!.pushViewController(newVC, animated: true) which I believe will solve my issue.
Assuming you implemented the parentNavigationController as they did in the demo, you are almost all set.
Delete your existing Segue - you won't be using it.
Give your DetailViewController a Storyboard ID - such as "DetailVC"
Change your code to instantiate the DetailVC and push it onto the parent Nav Controller
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let vc = self.storyboard?.instantiateViewController(withIdentifier: "DetailVC") as? DetailViewController {
vc.recipe = RecipeManager.shared.recipes[indexPath.row]
parentNavigationController!.pushViewController(vc, animated: true)
}
}
I want to passing data between TableViewController and ViewController
the program does not go into the method
My swift code:
override func unwind(for unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) {
let destView : ViewController = unwindSegue.destination as! ViewController
destView.min = Int(minTable)
destView.tableText = unitsText
}
I take data:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let tableCell = moneyArray[indexPath.row]
minTable = tableCell.val
unitsText = tableCell.name
let _ = navigationController?.popViewController(animated: true)
}
Adn my Table Code:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "LabelCell", for: indexPath) as! TableViewCell
let tableShow = moneyArray[indexPath.row]
cell.nameCurrency?.text = tableShow.name
cell.valueCarrency?.text = "\(tableShow.val)"
return cell
}
You are using popViewController on your didSelectRow, that means that you are returning on your navigation controller and not pushing a unwind segue or any segue, so you cant use prepareForSegue/unwind method.
One correct way of solving this is using delegation.
You can find more information about that here:
Passing data back from view controllers Xcode
But if you want to use unwind segue, you will have to write your unwind method on the previous viewController, not your current. Also you will need to use the method performSegue with the identifier of your unwind segue.
You can see more information about unwind segues here:
What are Unwind segues for and how do you use them?
If you want to open a detail view controller when the user clicks on a cell in your main table view controller then the proper way to pass data is by using something like the following:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "MyDetailView") {
// pass data to next view
if let viewController: MyDetailsViewController = segue.destinationViewController as? MyDetailsViewController {
viewController.units = mySelectedTableCell.unitsName
}
}
}
Full docs here.