I'm getting fatal error: unexpectedly found nil while unwrapping an Optional value when I segue from the cell.
Here's the code:
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 1 {
let listIngredients = recipeItem.ingredients[indexPath.row]
selectedIngredient = listIngredients.ingredient
}
tableView.deselectRowAtIndexPath(indexPath, animated: false)
performSegueWithIdentifier("showIngredientInRecipe", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showIngredientInRecipe" {
let svc = segue.destinationViewController as! UINavigationController
let destination = svc.topViewController as! IngredientDetailViewController
destination.ingredientItem = selectedIngredient
print("selectedIngredient \n \(selectedIngredient)")
}
}
Here's what I get from the debugger:
selectedIngredient
nil
selectedIngredient
Optional(Ingredient {
name = Rye Whiskey;
inStock = 1;
type = IngredientType {
name = Spirit;
};
})
fatal error: unexpectedly found nil while unwrapping an Optional value
As you can see, the selectedIngredient prints twice. First as nil, second time with the expected content. If i replace destination.ingredientItem = selectedIngredient with destination.ingredientItem = recipeItem.ingredients[0].ingredient the segue runs fine with no errors.
You're checking if indexPath's section is 1 and if it's not it will still perform segue. Make sure your selectable cells are in section 1 (or section 0?) and move your performSegueWithIdentifier("showIngredientInRecipe", sender: self) call to if statement to make it more safe.
Fixed:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showIngredientInRecipe" {
if let selectedIndexPath = self.tableView.indexPathForSelectedRow {
if selectedIndexPath.section == 1 {
let listIngredients = recipeItem.ingredients[selectedIndexPath.row]
selectedIngredient = listIngredients.ingredient
let svc = segue.destinationViewController as! UINavigationController
let destination = svc.topViewController as! IngredientDetailViewController
destination.ingredientItem = selectedIngredient
print("selectedIngredient \n \(selectedIngredient)")
}
}
}
}
Your code is error prone because you can perform segue regardless of
the section
A better way to get the selected item, is to call indexPathForSelectedRow() method inside of the delegate
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.section == 1 {
performSegueWithIdentifier("showIngredientInRecipe", sender: self)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "showIngredientInRecipe" {
//get selected item
let indexPath = self.tableView.indexPathForSelectedRow() {
let selectedIngredient = recipeItem.ingredients[indexPath.row]
print("selectedIngredient \n \(selectedIngredient)")
//segue
let svc = segue.destinationViewController as! UINavigationController
let destination = svc.topViewController as! IngredientDetailViewController
destination.ingredientItem = selectedIngredient
}
}
}
Related
Im currently creating an app that has a list of locations on a uitableview. There is also a filtering function so that the locations can be filtered based on categories. When not filtering, the uitableviewcell performs the segue when clicked and the correct data gets loaded in the destination viewcontroller. However when I filter tableview, the cells get populated with the right data, but when I click the cell, the destination viewcontroller always loads the data from the first cell in the UNFILTERED tableview.
The code that I am using:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "shoutoutCell", for: indexPath) as! shoutoutTableViewCell
var shout = shoutout[indexPath.row]
if isFiltering == true {
shout = filteredShoutout[indexPath.row]
} else {
shout = shoutout[indexPath.row]
}
cell.shoutout = shout
cell.showsReorderControl = true
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
var object = shoutout[indexPath.row]
if isFiltering == true {
object = filteredShoutout[indexPath.row]
} else {
object = shoutout[indexPath.row]
}
performSegue(withIdentifier: "shoutoutDetailSegue", sender: object)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "shoutoutDetailSegue" {
if let destination = segue.destination as? shoutoutDetailViewController {
// destination.shoutoutSelected = sender as! object
destination.shoutoutSelected = shoutout[(tableView.indexPathForSelectedRow?.row)!]
}
}
}
This is happening because you are using shoutout Array in prepare(for segue even when filtering.
destination.shoutoutSelected = shoutout[(tableView.indexPathForSelectedRow?.row)!]
Change it to
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "shoutoutDetailSegue" {
if let destination = segue.destination as? shoutoutDetailViewController {
// Here `Model` should be your class or struct
destination.shoutoutSelected = sender as! Model
}
}
OR move your logic here from didSelectRowAt
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "shoutoutDetailSegue" {
if let destination = segue.destination as? shoutoutDetailViewController {
if isFiltering == true {
object = filteredShoutout[indexPath.row]
} else {
object = shoutout[indexPath.row]
}
destination.shoutoutSelected = object
}
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.selectedIndexPath = indexPath as NSIndexPath
performSegue(withIdentifier: "toAddToChart", sender: nil)
}
open override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.selectedIndexPath
let product = products[indexPath.row]
if (segue.identifier == "toAddToChart") {
if let viewController = segue.destination as? AddToOrderVC {
viewController.productName = product.productName
viewController.productCode = product.productCode
viewController.productType = product.productType
viewController.productPrice = product.productPrice
viewController.productDetails = product.productDescription
viewController.pImageUrl = product.productimageUrl
}
}
}
/when i remove prepareForSegue function from viweController its will work but when i apply it and after build the code i get thread 1:signal SigBArt error when another action segue button pressed
When let product = products[indexPath.row]
called out side of the segue with confirm identifier,it will beget an error so the of this error is
open override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let indexPath = self.selectedIndexPath
if (segue.identifier == "toAddToChart") {
if let viewController = segue.destination as? AddToOrderVC {
let product = products[indexPath.row]
viewController.productName = product.productName
viewController.productCode = product.productCode
viewController.productType = product.productType
viewController.productPrice = product.productPrice
viewController.productDetails = product.productDescription
viewController.pImageUrl = product.productimageUrl
}
}
}
I'm trying to segue and pass data (from UITableView to a UIViewController). My code is
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "IndividualAchievementsSegue" {
let destination = segue.destinationViewController as? IndividualAchievementsViewController
let cell = sender as! UITableViewCell
let selectedRow = AchievementsTable.indexPathForCell(cell)!.row
destination!.viaSegue = achievements[selectedRow]
print(selectedRow)
}
}
but the line let cell = sender as! UITableViewCell is throwing
Could not cast value of type 'ProgramName.AchievementsViewController' to 'UITableViewCell'
I have also tried
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "IndividualAchievementsSegue" {
let destination = segue.destinationViewController as? IndividualAchievementsViewController
let selectedRow = AchievementsTable.indexPathForSelectedRow!.row
destination!.viaSegue = achievements[selectedRow]
print(selectedRow)
}
}
That fails because it finds nil unwrapping the optional during achievements[selectedRow] since that apparently gives me a nil from let selectedRow = AchievementsTable.indexPathForSelectedRow!.row
I think the problem is that your segue is connected with your AchievementsViewController to IndividualAchievementsViewController instead of UITableViewCell to IndividualAchievementsViewController. Check in the storyboard for that and correct the segue with TableCell to IndividualAchievementsViewController.
So the solution was to go into DidSelectrowAtIndexPath and remove DeselectRowAtIndexPath, which was in there twice by accident. Then use the following
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "IndividualAchievementsSegue" {
let destination = segue.destinationViewController as? IndividualAchievementsViewController
let selectedRow = AchievementsTable.indexPathForSelectedRow?.row
destination!.viaSegue = achievements[selectedRow!]
print(selectedRow)
}
}
How can I use indexPathForSelectedRow in CollectionView?
CollectionView/TableView:
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("levelCell", forIndexPath: indexPath) as! CollectionViewCell
guard levelListen != nil else {
return cell
}
let levelListe = levelListen[indexPath.row]
cell.levelLabel?.text = levelListe.name
return cell
}
TableView - prepareForSegue (work):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "zumLevel" {
let dstCtl = segue.destinationViewController as! InLevelViewController
let indexPath = tableView.indexPathForSelectedRow!
dstCtl.levelList = levelListen[indexPath.row]
}
}
CollectionView - prepareForSegue (not work):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "zumLevel" {
let dstCtl = segue.destinationViewController as! InLevelViewController
// like TableView?
let indexPath = collectionView.indexPathsForSelectedItems()!
dstCtl.levelList = levelListen[indexPath.row] // <--- .row doesn't work, should I use .count ?
}
}
When I try .count it says: "unexpectedly found nil while unwrapping an Optional value"
collectionView.indexPathsForSelectedItems() returns multiple IndexPath's because you can select multiple cells. This function returns an array of IndexPaths, e.g [NSIndexPath].
It should work if you'd do something like:
let indexPath = collectionView.indexPathsForSelectedItems()
if let lastRowSelected = indexPath?.last?.row {
dstCtl.levelList = levelListen[lastRowSelected]
}
I need to add a value to the selected row than in prepareForSegue that it does its action.But my prepareForSegue is doing the action before it.When i try to place the "performSegueWithIdentifier" it crashes,also i have added the id to the segue idenfifier in the storyboard.Im working with sqlite.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "editSegue")
{
let viewController : WorkoutView = segue.destinationViewController as! WorkoutView
viewController.workoutInfoData = marrStudentData.objectAtIndex(selectedRow) as! WorkoutInfo
navigationItem.title = ""
}
}
You can fetch the selected indexPaths from tableView in your prepare for segue
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "editSegue")
{
if let indexPath = tableView.indexPathForSelectedRow{
let viewController : WorkoutView = segue.destinationViewController as! WorkoutView
viewController.workoutInfoData = marrStudentData.objectAtIndex(indexPath.row) as! WorkoutInfo
navigationItem.title = ""
}
}
}