Reciever has no segue with identifier swift 2 - ios

I am trying to pass value from Realm via segue to another viewcontroller. The problem is it is crashing with error : Reciever has no segue with identifier. I tried other way, prepareforsegue but it is not working as well as this one.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let lektire = datasource[indexPath.row]
let destinationVC = DetaljiViewController()
destinationVC.programVar = lektire.ime
destinationVC.performSegueWithIdentifier("oLektiri", sender: self)
}
However, my segue is definitely there, and its identifier is properly set (see screen shot below).
What is going on, and how can I fix this problem?

I believe this is what you are looking for you have to call performSegueWithIdentifier from the view controller you are currently inside of and either make the sender the indexPath or you can make it your datasource item.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("oLektiri", sender: indexPath)
// Another WAY
self.performSegueWithIdentifier("oLektiri", sender: datasource[indexPath.row])
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "oLektiri") {
let indexPath = sender as! NSIndexPath
let toViewController = segue.destinationViewController as? NextViewController
let lektire = datasource[indexPath.row]
toViewController?.selectedObject = lektire
}
// Another WAY
if(segue.identifier == "oLektiri") {
let lektire = sender as? lektireTYPE
let toViewController = segue.destinationViewController as? NextViewController
toViewController?.selectedObject = lektire
}
}
// An example of what the next view controller should look like
class NextViewController: UIViewController {
var selectedObject: ObjectType?
}

Related

Swift/XCode - Why can't I pass data to the next view controller when a tableViewCell is pressed?

Apologies if this has already been answered elsewhere- I have spent the last two hours trying different things using similar suggestions but I still can't solve it!
Basically, I have a UITableView with custom cells(for different quiz topics) that when pressed, should allow the app to go to the next VC and pass an integer so the next VC knows which quiz to load. In my table view VC I have this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
rowPressed = indexPath.row
print ("rowPressed = \(rowPressed) before VC changes")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.destination is GameVC {
let vc = segue.destination as? GameVC
vc?.toPass = rowPressed
print("I ran...")
} else {
print("You suck")
}
and in my GameVC I have this:
var toPass = Int()
override func viewDidLoad() {
super.viewDidLoad()
print("toPass = \(toPass)")
The console output when run is this:
I ran...
toPass = 0
rowPressed = 3 before VC changes
So it looks like the VC changes before the previous one can send the correct value of toPass. How do I fix this?
Many thanks in advance!
According to the segue description:
For example, if the segue originated from a table view, the sender parameter would identify the table view cell that the user tapped.
So, sender is a UITableViewCell and you can do like below:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let cell = sender as? UITableViewCell else { return }
guard let idx = tableView.indexPath(for: cell) else { return }
guard let vc = segue.destination as? GameVC else { return }
vc.toPass = idx.row
}
The problem of your code was prepare(segue:) was invoked before tableView(didSelectRowAt:).
As suggested you have to call the performSegue method:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// not necessary
//rowPressed = indexPath.row
//print ("rowPressed = \(rowPressed) before VC changes")
performSegueWithIdentifier("Your id", sender: indexPath)
//You can set the identifier in the storyboard, by clicking on the segue
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Your id"{
var vc = segue.destinationViewController as! GameVC
vc.toPass = (sender as! IndexPath).row
}
}
Because prepareForSegue is called before didSelectRowAt, you can also remove the segue from the Storyboard, drag and drop holding ctrl from the UITableViewController's yellow icon in the top to the second ViewController, click on the segue, give it an identifier, so now you can call performSegue:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.pressed = indexPath.row
self.performSegue(withIdentifier: "segue identifier", sender: nil)
}

Passing data from table view cell to new view controller error

I'm trying to pass data from my tableViewCell, however, keep getting an error on my let cell = sender as! UITableViewCell
fatal error: unexpectedly found nil while unwrapping an Optional
value.
How would I make my cell an optional? I'm trying to pass the data from an array.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destination = segue.destination as? UsersProfileViewController{
let cell = sender as! UITableViewCell
let selectedRow = partnerTable.indexPath(for: cell)?.row
if let name = usersArray[selectedRow!].firstLastName{
destination.name = name
}
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "toUser", sender: nil)
}
}
You should trigger this way
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.performSegue(withIdentifier: "toUser", sender: indexPath.row)
}
and in prepare for segue
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
let selectedRow = sender as? Int
if let name = usersArray[selectedRow!].firstLastName{
destination.name = name
}
}
If you prefer to use segue using storyboard:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableView.indexPathForSelectedRow {
guard let destinationVC = segue.destination as? UsersProfileViewController else {return}
let selectedRow = indexPath.row
destinationVC.name = usersArray[selectedRow].firstLastName
}
}
You can also segue to the destination ViewController programatically and pass the required value to that Controller.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let sb = UIStoryboard.init(name: "StoryboardName", bundle: nil)
let destinationVC = sb.instantiateViewController(
withIdentifier: "StoryboardId for viewcontroller set in storyboard") as? UsersProfileViewController
guard let name = usersArray[selectedRow!].firstLastName { else return }
destinationVC.name = name
self.presentViewController(destinationVC, animated: true, completion: nil)
}
Steps:
Get an instance of the storyboard.
Instantiate the required viewcontroller with the help of storyboard Id which is assigned in the storyboard.
Pass the data required to destination VC.
Present the destination View Controller.
I got one thing from you post, you want to get current selected tableview data and want to pass it to next view controller, you can do this in a simple way. Check tableview cell selected delegate method
didSelectRowAtIndexPath
From this delegate method you can save selected row index and in prepare segue method call this function in this way , selectedRow value you can set in didSelectRowAtIndexPath delegate method.
usersArray[selectedRow!].firstLastName
Steps To Do:
First of all create the property selectedIndex in View controller.
set the selectedIndex property value with indexPath from didSelectRowAtIndexPath table view's delegate methods.
In Prepare for Segue method you can find the data value from your datasource object as you have selected index.
4.Pass the value from datasource for selected index and make selectedIndex property to nil.

TableViewCell to ViewController - Swift

I tried many ways but still can't go to another ViewController. This is my storyboard.
and This is my code.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HistoryMainTableViewCell {
listItems.sort() { $0.seq > $1.seq }
let history = listItems[indexPath.row]
cell.setValue(history: history)
return cell
} else {
return HistoryMainTableViewCell()
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
if let productHistoryPage = segue.destination as? HistoryProductViewController {
if let IndexPath = tableViewHistory.indexPathForSelectedRow {
let historyArray = listItems[IndexPath.row]
productHistoryPage.history = historyArray
}
}
}
}
Try this code:(Untested Code)
Note: Below code will trigger prepare segue. When, tableViewCell selected.Let me know the code works...
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
// let indexPath = myTableView!.indexPathForSelectedRow
// let currentCell = tableView.cellForRow(at: indexPath)! as UITableViewCell
//sendSelectedData = (currentCell.textLabel?.text)! as String as NSString
performSegue(withIdentifier: "vcProductHistory", sender: self)
}
Based on your answer to my initial comment: dragging the line from initial vc to destination vc tells your vc about the segue, preparing for segue tells your vc what do when the segue is triggered, but you still need to let your vc know when it should perform the segue. try this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell") as? HistoryMainTableViewCell else { return HistoryMainTableViewCell() }
let history = listItems[indexPath.row]
cell.setValue(history: history)
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
guard let productHistoryPage = segue.destination as? HistoryProductViewController, let indexPath = sender as? IndexPath else { return }
let historyArray = listItems[indexPath.row]
productHistoryPage.history = historyArray
}
}
func tableView(_ tableView: UITableView, didSelectRowAtIndexPath indexPath: IndexPath) {
performSegue(withIdentifier: "vcProductHistory", sender: indexPath)
}
I think this is a little bit neater using the guard syntax. Also, it uses sender as the indexPath, not using tableView.indexPathForSelectedRow.
Also, I noticed you have this in your cellForRowAtIndexPath:
listItems.sort() { $0.seq > $1.seq }
Your sorting your data source in cellForRowAtIndexPath. You can't do that there because that function gets called for each row individually. You need to do the sorting before that is called, somewhere like viewWillAppear. Also, to sort an array in place you need to remove the () after sort. try this.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
listItems.sort { $0.0.seq > $0.1.seq }
tableView.reloadData()
}
I think you should connect to UINavigationController , not to HistoryProductionViewController
Because your MainViewController's segue connected to Navigation Controller
I guess in func prepareForSegue segue.identifier == "vcProductHistory" it works but, if let productHistoryPage = segue.destination as? HistoryProductViewController not works
because your destination ViewController's class is NavigationController
you should call
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "vcProductHistory" {
if let naviVC = segue.destination as? UINavigationController {
if let productHistoryPage = naviVC.topViewController as? HistoryProductViewController {
if let IndexPath = tableViewHistory.indexPathForSelectedRow {
let historyArray = listItems[IndexPath.row]
productHistoryPage.history = historyArray
}
}
}
}
}
Your solution - as you described it - should work.
The most work here can be done directly in the Storyboard.
Select the prototype cell in the TableView, drag the Segue to the NavigationController and select presentModally.
This should work without further magic.
When selecting the TableViewCell the NavigationController should be presented. There is also no code needed at this point (except for the population of the TableView).
If not - you maybe misconfigured your TableView by setting the selection to No Selection:
or you set User Interaction Enabled to false somewhere.

Swift 2.1 - How to pass index path row of collectionView cell to segue

From the main controller that I have integrated collection view, I want to pass selected cell index path to another view controller (detail view)
so I can use it for updating a specific record.
I have the following working prepareForSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "RecipeDetailVC" {
let detailVC = segue.destinationViewController as? RecipeDetailVC
if let recipeCell = sender as? Recipe {
detailVC!.recipe = recipeCell
}
}
}
And I've tried including let indexPath = collection.indexPathForCell(sender as! UICollectionViewCell) but I get Could not cast value of type 'xxx.Recipe' (0x7fae7580c950) to 'UICollectionViewCell' at runtime.
I also have performSegueWithIdentifier("RecipeDetailVC", sender: recipeCell) and I wonder if I can use this to pass the selected cell's index path but not sure I can add this index to the sender.
I am not clear about the hierarchy of your collectionViewCell. But I think the sender maybe not a cell. Try to use
let indexPath = collection.indexPathForCell(sender.superView as! UICollectionViewCell)
or
let indexPath = collection.indexPathForCell(sender.superView!.superView as! UICollectionViewCell)
That may work.
I've wrote up a quick example to show you, it uses a tableView but the concept is the same:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var things = [1,2,3,4,5,6,7,8] // These can be anything...
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = UITableViewCell()
let objectForCell = self.things[indexPath.row] // Regular stuff
return cell
}
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let objectAtIndex = self.things[indexPath.row]
let indexOfObject = indexPath.row
self.performSegueWithIdentifier("next", sender: indexOfObject)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "next" {
// On this View Controller make an Index property, like var index
let nextVC = segue.destinationViewController as! UIViewController
nextVC.index = sender as! Int
}
}
}
Here you can see you get the actual object itself and use it as the sender in the perform segue method. You can access it in prepareForSegue and assign it directly to a property on the destination view controller.

Button Segue in custom UITableViewCell

I have a custom UITableViewCell with a button inside to segue the data to another viewController. This is by utilizing the
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
//cell setup code
cell.button.tag = indexPath.row
cell.button.addTarget(self, action: "editRow", forControlEvents: .TouchUpInside)
return cell
}
then using the function
func editRow() {
self.performSegueWithIdentifier("editRow", sender: self)
}
the editRow segue is a storyboard segue.
The issue...Instead of performing the segue and passing the data, it fires the segue twice and doesn't transfer any data to the "SLEdit" viewController. Any ideas as to why this is happening? Please keep in mind I'm not very familiar with objective-c due to beginning learning with swift. If you need any further information or more code let me know. Thanks!
EDIT:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "editRow" {
let cell = sender as! SLTableViewCell
let indexPath = tableView.indexPathForCell(cell)
let SListController:SLEdit = segue.destinationViewController as! SLEdit
let items:SList = frc.objectAtIndexPath(indexPath!) as! SList
SListController.item = items
}
}
This is because you have already made a segue action in storyboard editor and you are calling the same segue again by assigning custom action to the button.
And to pass data to the next view controller use this method:
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!){
if segue.identifier == "segueIdentifier" {
}
}
Edit 1:
The error: "Could not cast value of type UIButton" is due to this line:
let cell = sender as! SLTableViewCell
Here the sender UIButton and you are convertring/assigning(whichever term is suitable) to the TableViewCell.
Edit 3:
Replace your code by this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!{
if segue.identifier == "editRow" {
let button = sender as! UIButton
let view = button.superview!
let cell = view.superview as! <Your custom cell name here>
let indexPath = itemTable.indexPathForCell(cell)
let SListController:SLEdit = segue.destinationViewController as! SLEdit
let items:SList = frc.objectAtIndexPath(indexPath) as! SList
SListController.item = items
}
}

Resources