I am implementing the following. I have a view controller and I have buttons that open a table view controller with selection items. when I then select an item in a cell and go back to the view controller, previous values are blank (looks like it opens a new instance). The code I am using to go back is as follows:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "playerselected" {
let cell = sender as! UITableViewCell
let indexPath = tableView.indexPath(for: cell)
let itemController : GameViewController = segue.destination as! GameViewController
let item : Player = frc.object(at: indexPath!) as! Player
itemController.item2 = item
}
Yes, you're right, if you have segue from VC1 (GameViewController) to VC2 (SomeTableViewController) and then from VC2 to VC1, it's not the same instance of VC1.
If you don't use UINavigationController, you can simply "go" from VC2 back to VC1 by dismissing your VC2 using
dismiss(animated: true)
But how can you now pass data back to your previous controller? Start with this: in VC2 create closure variable which takes parameter of type Player. Then before you dismiss VC2, call this closure
class SomeTableViewController: UITableViewController {
var callback: ( (Player) -> Void )?
func playerSelected(_ player: Player) {
callback?(player)
dismiss(animated: true)
}
}
But will happen after callback is called? Nothing, you have to assign what will happen before you present VC2. So you can override prepare(for:sender:) inside VC1 and assign destination's callback closure variable (when callback will be called, you need to change VC1's item2)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueToTableViewController" {
let tableViewController = segue.destination as! SomeTableViewController
tableViewController.callback = { player in
self.item2 = player
}
}
}
Related
I have two views that I would like to pass data from one view to the next. The first view is where I have the data that I would like to pass to the next view lets call it FirstViewController. However FirstViewController is embedded in a NavigationViewController and the secondViewController lets call it DestinationViewController.
I have tried this logic
IN FirstViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "DestinationViewController") {
if let vc: DestinationViewController = segue.destination as? DestinationViewController {
vc.id = id
}
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if indexPath.section == 1
{
self.id = ((self.categorytArray.object(at: indexPath.row)as AnyObject).value(forKey: "id")as! NSNumber)
print(self.id)
self.performSegue(withIdentifier: "DestinationViewController", sender: self)
}
}
on destinationController I get empty value for id.
if I change in this line
if let vc: DestinationViewController = segue.destination as! DestinationViewController
I get this error:
Could not cast value of type 'UINavigationController' (0x10f9b3760) to 'Name.DestinationViewController' (0x1067f7c68).
2019-10-11 12:52:11.535900+0530 Name[2412:94748] Could not cast value of type 'UINavigationController' (0x10f9b3760) to 'Name.DestinationViewController' (0x1067f7c68).
Thanks in advance.
Since your View Controller is embedded in a Navigation Controller you need to retrieve it from the Navigation Controller as well
Try this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let nvc = segue.destination as? UINavigationController, let vc = nvc.viewControllers.first as? DestinationViewController else {
return
}
vc.id = id
}
First you retrieve the navigation controller (nvc), then you get your destination vc from it's list of child view controllers. Generally, the first controller in the Navigation Controller will be first in the array.
Also you don't need to provide a sender in your performSegue call.
self.performSegue(withIdentifier: "DestinationViewController", sender: nil)
You will notice that prepareForSegue has a sender parameter, but since you are accessing the information you want to pass to the DetailViewController through a class variable in your first View Controller, you're not using this sender parameter anyway.
Hope this helps
I have View Controller A, which has five static collection view cells, with a title label, and a description label. Based on which cell was tapped, I need to segue to View Controller B, and View Controller B will present a list of products associated with that data.
I attempted to do this is didSelect method, but I think I am wrong... I realized after using print statements I am correctly getting the navigation to work, and I can also print the name label on View Controller A, but the data passed to View Controller B is nil.
View Controller A
var parameters: [Parameter] = [
Parameter(name: "Alkalinity", description: "A description here is about how important it is to have a stable measurement"),
Parameter(name: "Calcium", description: "A description here is about how important it is to have a stable measurement"),
// Cut the amount so I do not take too much space here
]
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
let selectedCell = parameters[indexPath.row]
// Create an instance of DeatailViewController and pass that variable
let destinationVC = DetailViewController()
destinationVC.dataReceived = selectedCell.name
self.performSegue(withIdentifier: "segueID", sender: self)
}
View Controller B
only a print statement.
Expected: Pass the name of the cell I tapped to the second VC, (For now) but I want to show a list of products associated with each element in name label. ex: Alkalinity will show Alkalinity products ( should I define this in the same of different model)?
Error:
Showing nil on VCB
Suggestions:
Maybe using index path in didSelectRow?
When using a segue to pass data you need to implement prepareForSegue
override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "segueID", sender:parameters[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueID" {
let vc = segue.destination as! DetailViewController
destinationVC.dataReceived = sender as! Model // Model is type of array parameters
}
}
but with vc instantiation DetailViewController() you need to use present/push
let destinationVC = DetailViewController()
destinationVC.dataReceived = selectedCell.name
self.present(destinationVC,animated:true)
For second way presenting DetailViewController() will crash the app as you don't load the vc from storyboard , so it should be like
let vc = self.storyboard!.instantiateViewController(withIdentifier: "DetailID") as! DetailViewController
If you are using segue, I would achieve that by doing something like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segueID" {
if let destinationVC = segue.destination as? DetailViewController {
destinationVC.dataReceived = selectedCell.name
}
}
}
How do you segue from a ViewController to a ViewController located in a UITableView (which is in a NavigationController)?
Image of Storyboard with description
I can Control-Drag from my "DetailButton" to the "DetailViewController", but it is not displayed in the navigation controller if you do that. The NavigationBar should still allow a user to go back to the UITableView, even if they segued here from the "DetailButton".
Here's the solution I came up with. If anyone else has a better way, please post it here.
Storyboard Steps:
Control-Drag from the "DetailButton" to the Navigation Controller to create a segue. Give this segue the identifier "detailSegue".
Control-Drag from the UITableViewCell you would like to link to DetailViewController to the DetailViewController. Give this segue the name "showDetail".
UITableViewController Steps:
var shouldSegueToDetail = false
override func viewDidLoad() {
...
// Check if we should segue to DetailViewController
if shouldSegueToDetail {
// Reset flag
shouldSegueToDetail = false
// Go to DetailViewController
performSegue(withIdentifier: "showDetail", sender: self)
}
}
OriginalViewController Steps:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "detailSegue" {
let navigationVC = segue.destination as! UINavigationController
let tableVC = navigationVC.viewControllers[0] as! UITableViewController
tableVC.shouldSegueToDetail = true
}
}
Option:
Instead of making a segue from the UITableViewCell to the DetailViewController, you could probably call UITableView's selectRow method with the IndexPath of the item you want selected.
I have a collection view. each item should pass data to second view, in other world I want to pass current view to second view by navigationController.
class GroupsViewController: UIViewController ,UICollectionViewDataSource, UICollectionViewDelegate {
....
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
// handle tap events
var group = self.items[indexPath.item]
var cat_title = group.cat_title
self.navigationController!.pushViewController(self.storyboard!.instantiateViewController(withIdentifier: "GroupSubOneTableViewController") as UIViewController, animated: true)
}
....
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
print("ooopps")
if (segue.identifier == "GroupSubOneTableViewController") {
let navController = segue.destination as! UINavigationController
let detailController = navController.topViewController as! GroupSubOneTableViewController
//detailController.currentId = nextId!
print("selected GroupSubOneTableViewController")
}
}
but in console log doesn't show anything!
You are mixing here two thing perfomSegue and pushViewController. performSegue(withIdentifier:sender:)
is called when you perform a segue using performSegue. So create one segue from your SourceVC to DestinationVC.
If you want to go with pushViewController then simply type cast UIViewController to your ViewController pass the data.
let vc = self.storyboard!.instantiateViewController(withIdentifier: "GroupSubOneTableViewController") as! GroupSubOneTableViewController
//pass vaue
vc.passStr = "Hello"
self.navigationController?.pushViewController(vc, animated: true)
It's not called because pushViewController doesn't perform a segue.
You need to call performSegue(withIdentifier: "GroupSubOneTableViewController", sender: self) instead of pushViewController.
Of course, you'll also need to set this up in your storyboard.
I have created a popover in iphone view with a button inside to segue to another ViewController.
But all Viewcontroller after popOver have the navigationbar not anymore.
My project structure:
NavigationController -> ViewController1 -> ViewController2(popOver) ->ViewController3
All connections are: "show",except from ViewController1 to ViewController2: "present as popover"
If i connect(show) directly from ViewController1->ViewController3, everything is fine...
Where can be the problem?
I used this tutorial:
http://richardallen.me/2014/11/28/popovers.html
ViewController1:
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return UIModalPresentationStyle.None
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "PopOverIdentifier" {
let popoverViewController = segue.destinationViewController as!
popoverViewController.modalPresentationStyle = UIModalPresentationStyle.ViewController2
popoverViewController.popoverPresentationController!.delegate = self
}
}
ViewController2
includes Pickerview and:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toVC3" {
var DestViewController : ViewController3 = segue.destinationViewController as!ViewController3
DestViewController.passedUserID_target = selected_user
}
}
Problem:
I solved this problem (temporarily) with adding a new NavigationController to the ViewController3 with (Editor->EmbedIn->NavigationController) and add a back button to buttom of the layout.
The problem to segue data from VC1->VC3, i solved with defining a global Variable in VC3 and add a action Button (in VC2) with assigning the data to these created global variables.