I'm trying to pass an object from one view controller to another, but the receiver receives nil and throws an error saying:
Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "sPropSegue" {
if let indexPath = tableView.indexPathForSelectedRow {
let prop = modelProperty.props[indexPath.row]
let dVeiwController = segue.destination as? PDViewController
detailVeiwController?.property = prop
}
}
}
code in the UIViewController
var property: Property!
override func viewDidLoad() {
super.viewDidLoad()
propImage.image = UIImage(named: property.imageName)
priceLable.text = property.price
addressTextView.text = property.address //propImage.image = UIImage()
// Do any additional setup after loading the view.
}
please help me solving this problem.
Thank you in advance!
You need to make sure segue is established from the Source vc ( not the table cell ) to the destination vc for override func prepare(for segue to be called
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier:"sPropSegue",sender:nil)
}
According to my understanding you are not getting property object in your PDViewController.
So try to debug it. Make sure that prop is not nil.
Try executing the same code after commenting viewDidLoad() code.
Also make sure you have self.table.delegate = self in the controller where you are trying to perform segue.
Related
I'm building an app for school. The app is sort of a dictionary app that includes a table of vocabulary words (my WordsTableViewController) embedded into a navigation controller, and my Definition View Controller which shows the vocab word and its corresponding definition.
The problem is that I have no idea how to use indexpathforselectedrow to get the appropriate vocabulary word from my vocabController's vocabWords array. The 'Vocabulary Words' are struct instances located in its own separate swift file.
All of it will be in my prepare for segue inside of my WordsTableViewController swift file.
Any help is really appreciated.
The table views number of rows looks like this:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myListVocab.vocabWords.count
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowDefinitionSegue" {
if let definitionVC = segue.destination as? DefinitionViewController {
let index = tableView.indexPathForSelectedRow
let indexPathForSelectedRow = index.row
definitionVC.vocabWord[index]
let vocab = myListVocab.vocabWords[indexPathForSelectedRow]
}
}
}
The error I'm getting is that I have an ambiguous reference to member tableView(_:numberOfRowsInSection:)
The rest of my errors in my program so far is 'override can be specified only on class members'
First implement didSelectRow in your WordsTableViewController. Then create instance variable selectedWord. After that, you can get the the selected word in didSelectRow by calling:
selectedWord = myListVocab.vocabWords[indexPath.row]
After assignment, you can use selectedWord in prepareForSegue
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ShowDefinitionSegue" {
if let definitionVC = segue.destination as? DefinitionViewController {
destinationVC.word = selectedWord
}
}
}
Instead of doing it in prepare(for:sender:) push the DefinitionViewController manually in UITableViewDelegate's tableView(_: didSelectRowAt:) method, i.e
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let definitionVC = self.storyboard?.instantiateViewController(withIdentifier: "DefinitionViewController") as? DefinitionViewController {
let word = myListVocab.vocabWords[indexPath.row] //get the word using indexPath...
definitionVC.word = word //set the word in definitionVC's property...
self.navigationController?.pushViewController(definitionVC, animated: true)
}
}
Don't forget to set the Storyboard ID of DefinitionViewController as DefinitionVC in the storyboard.
Also, remove the segue that you created in the storyboard. Otherwise, the DefinitionViewController will be pushed twice unnecessarily.
The prepare(for segue: ) function runs, but the data is not sent to my destination ViewController. I am getting following error when loading that VC's collection view which has to receive the data:
Unexpectedly found nil while unwrapping an Optional value
I have confirmed that the segue type is a selection segue, the segue identifier is correct, and that the views work as far as displaying dummy data.
First view controller (DrawerVC):
let realm = try! Realm()
var allDrawers: Results<Drawer>?
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("goToTool segue")
performSegue(withIdentifier: "goToTool", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "goToTool" {
print("Prepare for segue from drawerVC ran") // this runs!
let destinationVC = segue.destination as! ToolVC
if let indexPath = tableView.indexPathForSelectedRow {
destinationVC.selectedDrawer = allDrawers?[indexPath.row]
print("Selected drawer set to index path") // doesn't run :(
}
} else if segue.identifier == "goToEditCategory" {
_ = segue.destination as! EditCategoryVC
}
}
Destination view controller (ToolVC):
let realm = try! Realm()
var allTools: Results<Tool>?
var selectedDrawer: Drawer? {
didSet{
// doesn't run since selectedDrawer was never set :(
print("The selected drawer changed from \(oldValue) to \(selectedDrawer?.title)")
loadTools()
}
}
func loadTools() {
allTools = selectedDrawer?.tools.sorted(byKeyPath: "title", ascending: false)
toolCollectionView.reloadData() // this is where I get the optional value error
}
You are making a very common mistake.
In the destination view controller you are accessing the table view in loadTools() which is not connected yet at the moment you assign the selected drawer. Therefore the table view is nil and causes the crash.
A solution is to remove the didSet observer
var selectedDrawer: Drawer?
and load the data in viewDidLoad
func viewDidLoad() {
super.viewDidLoad()
if let drawer = selectedDrawer {
allTools = drawer.tools.sorted(byKeyPath: "title", ascending: false)
toolCollectionView.reloadData()
}
}
And if the segue is connected from the cell to the destination view controller remove the didSelectRowAt method.
I want to move from one view controller to another and send userId:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("chosenPerson", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destinationViewController as? chosenPersonViewController
let indexPaths = self.collectionView!.indexPathsForSelectedItems()!
let indexPath = indexPaths[0] as NSIndexPath
chosenPerson!.userID = self.usersArray[indexPath.row].userId
}
by clicking I get: "fatal error: unexpectedly found nil while unwrapping an Optional value"
what I do wrong?
If you have given segue in StoryBoard DON'T Call this self.performSegueWithIdentifier("chosenPerson", sender: self) method in didSelectItem
If you have given segue in storyboard override func prepareForSegue - this method calls first after that didSelectItem calls
Please refer storyBoard once (below Image for Sample)
I think problem is in self.usersArray[indexPath.row].userId May this returns nil
Swift2:
self.performSegueWithIdentifier("chosenPerson", sender: self)
Swift3:
self.performSegue(withIdentifier: "chosenPerson", sender: self)
Swift2:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destinationViewController as? chosenPersonViewController
let indexPaths = self.collectionView!.indexPathsForSelectedItems()!
let indexPath = indexPaths[0] as NSIndexPath
chosenPerson!.userID = self.usersArray[indexPath.row].userId //May it found nil please re - check array values
}
Swift3:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "chosenPerson" {
let chosenPerson = segue.destination as! chosenPersonViewController
if let indexPath = collectionView.indexPathForSelectedItem {
chosenPerson!.userID = self.usersArray[indexPath.row].userId //May it found nil please re - check array values
}
}
}
When performing the segue, pass the indexPath as the sender and try using this switch statement. If you see "unknown segue" printed out when you select a cell, the destination controller is not of type chosenPersonViewController.
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
performSegueWithIdentifier("chosenPerson", sender: indexPath)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch (segue.destinationViewController, sender) {
case (let controller as chosenPersonViewController, let indexPath as NSIndexPath):
controller.userID = usersArray[indexPath.row].userId
default:
print("unknown segue")
break
}
}
One possible issue could be that in your storyboard you have not set the identifier for your segue to be "chosenPerson". To fix this first make sure you have a segue between your first and second view controllers, which can be created by control dragging one from one view controller to another.
Then make sure the segue has an identifier, specifically: "chosenPerson". To do this: click on the segue between the first two view controllers, then navigate to the attributes tab, and then set the identifier box to be "chosenPerson". Save the storyboard with your changes and now you should be able to call prepareForSegue with that identifier and not experience a fatal error.
I'm trying to pass the value to a second view controller
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "headToDetail" {
let indexPath = self.tableView.indexPathForSelectedRow!.row
print("SELECTED INDEX \(indexPath)")
let destVC = segue.destinationViewController as! SecondViewController
print("Segue TEst \(self.toPass)")
destVC.vieSegue = self.toPass
}
I've remove the didSelectRowAtIndexPath and just going to use the Segue but when I tap on a row cell I get:
fatal error: unexpectedly found nil while unwrapping an Optional value
This is a major update to the original code.
the problem is, that you are creating a new ViewController at line
var destination = SecondViewController()
but then the controller simply deallocates because nothing is pointing to it (there is no reference to it and it falls out of scope). You haven't pushed into it, nor you performed any segue.
How to fix it? Basically there are two options.
Storyboard
If you're working with storyboards, you have to create a segue between the first ViewController and SecondViewController. You have to give it an identifier.
Then in didSelectRowAtIndexPath you will write
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print(fruits[row])
self.toPass = fruits[row]
performSegueWithIdentifier("YOUR_SEGUE_IDENTIFIER", sender: nil)
}
then in prepareForSegue you'll set the property to the destination SecondViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "YOUR_SEGUE_IDENTIFIER" {
let destVC = segue.destinationViewController as! SecondViewController
destVC.vieSegue = self.toPass!
}
}
Manual Push
The second option is to push the controller manually within the didSelectRowAtIndexPath method
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print(fruits[row])
let destination = SecondViewController()
destination.vieSegue = fruits[row]
// if you are using navigation controller
navigationController?.pushViewController(destination, animated: true)
// if you want to present it modally
// presentViewController(destination, animated: true, completion: nil)
}
The second version doesn't force you to create segues.
It's empty because you create a new object...
var destination = SecondViewController()
...pass it your data...
destination.vieSegue = self.toPass!
...and then let it go out of scope without showing it, saving it, or doing anything else useful.
I get an error trying to set indexPath: initializer for conditional binding must have optional type
override func prepareForSegue(segue: UIStoryboardSegue,
sender: AnyObject?) {
if segue.identifier == "toStation" {
let detailViewController = segue.destinationViewController
as! toStation
if let indexPath = tableView.indexPathForSelectedRow! {
detailViewController.selectedStation = stationNameArray[indexPath.row]
}
}
}
if let indexPath = tableView.indexPathForSelectedRow!
You are binding indexPathForSelectedRow to indexPath view an if let statement to unwrap the optional indexPathForSelectedRow. This is a good approach. But at the same time you are using the bang statement ! to unwrap the optional. Effectively you are unwrapping the optional twice. Either use ! or an if let statement.
You receive this error because tableView.indexPathForSelectedRow! is no longer an optional value (you've unwrapped it). To fix this error, try removing the force unwrap operator ! at the end of if let.
if let indexPath = tableView.indexPathForSelectedRow { ... }
According to your question subject, I think you are trying to pass Indexpath into prepareForSegue, in your case, you can use didSelectRowAtIndexPath to pass a sender:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
self.performSegueWithIdentifier("toStation", sender: indexPath);
}
then in prepareForSegue :
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
if (segue.identifier == "toStation") {
let detailViewController = segue.destinationViewController
as! toStation
let row = (sender as! NSIndexPath).row; //we know that sender is an NSIndexPath here.
detailViewController.selectedStation = stationNameArray[row]
}
}
Hope this solve your problem.