Access IndexPath.row outside of didSelectRowAt indexPath function [duplicate] - ios

This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 3 years ago.
I am defining this in a UIViewController and trying to access myIndex in a different view controller. How can I do this?
I would like to do this programmatically without storyboards. Any help is much appreciated!
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//NSLog("You selected cell number: \(indexPath.row)!")
let myIndex = indexPath.row
}

You have to consider passing Indexpath like this:
performSegue(withIdentifier: "NextView", sender: indexPath)
func prepareForSegue(segue: UIStoryboardSegue, sender: Any) {
let indexPath = sender as! IndexPath
let eventStruct = post[indexPath.row]
let secondViewController = segue.destination as? NextViewController
secondViewController?.data = sender as AnyObject
}

If you want to access from anywhere of your project you can store the data in userdefault. inside your didSelectRowAt set the indexPath.row using key myIndex , this key should be unique:
UserDefaults.standard.set(indexPath.row, forKey: "myIndex")
Then retrieve the data from any viewController:
If UserDefaults.standard.object(forKey: "myIndex")!=nil{
let savedIndexPath = UserDefaults.standard.integer(forKey: "myIndex")
}

Related

How to preserve the original indexPath.row after applying filters to Table View?

My app uses "filter" buttons in which the whereField query is refined based on which filter buttons are pressed. This is an example before filtering:
But this is an example after filtering:
The issue is that when I click into one of the Rows, it takes me to the next page that corresponds to the original indexPath.row in my database belonging to that Row. How can I preserve the original indexPath.row? E.g., Cell B to always be indexPath.row = 1, even after filtering.
This is my cellForRowAt of my first View Controller.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get a cell
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
// Get the mealPlan that the tableView is asking about
let mealPlanInTable = mealPlan[indexPath.row]
// Customize the cell
cell.displayMealPlan(mealPlanInTable)
// Return the cell
return cell
}
And how I connect this View Controller's indexPath.row to the next View Controller after a cell is tapped:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Detect the indexPath the user selected
let indexPath = tableView.indexPathForSelectedRow
// Get the mealPlan the user selected
let mealPlanSelected = mealPlan[indexPath!.row]
// Get a reference to the NextiewController
let NextVC = segue.destination as! NextViewController
// Get a reference to the currentMealPlanIndex in the NextViewController
NextVC.currentMealPlanIndex = indexPath!.row
}
Any advice is much appreciated!
You are getting values from wrong array. Also it's better to pass the obj instead of index.
You need to have 2 variables - one for all data & other for filtered data.
Use filtered data var in tableview datasource & for passing to NextVC.
Considering your class name is MealPlan. Here is the source.
var allMealPlans: [MealPlan]
var filteredMealPlans: [MealPlan]
func onFilterButtonPressed() {
filteredMealPlans = allMealPlans.filter({
// return true/false here based on your filters
})
tableView.reloadData()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// Get a cell
let cell = tableView.dequeueReusableCell(withIdentifier: "MealPlanCell", for: indexPath) as! MealPlanCell
// Get the mealPlan that the tableView is asking about
let mealPlanInTable = filteredMealPlans[indexPath.row]
// Customize the cell
cell.displayMealPlan(mealPlanInTable)
// Return the cell
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Detect the indexPath the user selected
let indexPath = tableView.indexPathForSelectedRow
// Get the mealPlan the user selected
let mealPlanSelected = filteredMealPlans[indexPath!.row]
// Get a reference to the NextiewController
let NextVC = segue.destination as! NextViewController
// Get a reference to the currentMealPlanIndex in the NextViewController
NextVC.currentMealPlan = mealPlanSelected
}
Add a variable in your NextVC for currentMealPlan
class NextVC: UIViewController {
var currentMealPlan: MealPlan?
}
Thank you all for the comments/advice! Instead of connecting the data in the view controllers through the indexPath, I used a document ID that is consistent with the data flowing between my view controllers. This works with all of my filtering.
This is in my first ViewController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let indexPath = tableView.indexPathForSelectedRow {
let ingredientsVC = segue.destination as! IngredientsViewController
let documentID = mealPlan[indexPath.row].docID
ingredientsVC.currentMealPlanIndex = indexPath.row
ingredientsVC.passedDocID = documentID!
}
}
And this is in my second ViewController:
// This variable references the unique Document ID
var passedDocID = ""
// This is how I use that document ID to get a reference to the appropriate data
let selectedMealPlanIndex = mealPlan.firstIndex(where: {$0.docID == passedDocID})
let currentMealPlan = mealPlan[selectedMealPlanIndex!]

How to uniquely identify each cell in UITableView

I have a UITableView and I am trying to make a segue to another viewcontroller, I need the row number so that I can select a string from an array so that I can display this string in the next view controller, I have this code at the moment.
let tableFrontView = segue.destination as! FCTableFrontViewController
tableFrontView.frontText = path[FlashCardsTableViewCell.init().tag].flashCardFront
the FlashCardsTableViewCell.init().tag is currently returning an int for testing purposes though I am wanting to know what I can replace it with to get me the number of the row which was selected by the user.
Thanks
You can try like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let tableFrontView = segue.destination as! FCTableFrontViewController
let selectedIndexPath = tblView.indexPathForSelectedRow
let selectedRow = (selectedIndexPath?.row)!
print(selectedRow)
}
I assume you are writing the code snippet shown in prepare(for:)?
If that's the case, go to where you perform the segue, which is likely in the didSelectedRowAtIndexPath delegate method. If you don't have such a method, you should implement it.
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "showTableFrontVC", sender: nil)
}
Replace whatever it is that you are passing as sender now, and replace that with indexPath.row.
performSegue(withIdentifier: "showTableFrontVC", sender: indexPath.row)
Now in prepare(for:), you can unwrap sender as an Int:
let tableFrontView = segue.destination as! FCTableFrontViewController
let rowSelected = sender as! Int
tableFrontView.frontText = path[rowSelected].flashCardFront
Use TableView's delegate method :
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// indetify cell using indexPath.row attribute
}

Passing information to new view controller [duplicate]

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
}
}
}

pass the data from uitableview to other uiview

I'm new in swift and IOS, i have some problem to pass the dictionary data to other uiview, anyone can help me to fix it?
LessonsTableViewController:
var mylessons = [
["title":"Posture", "subtitle":"Set up your body", "bgimage":"1", "lesimage":"l1"],
["title":"Breathing", "subtitle":"Breathing deeply", "bgimage":"2", "lesimage":"l2"],
["title":"Breathing", "subtitle":"Breathing Exercise", "bgimage":"3", "lesimage":"l3"],
["title":"Health", "subtitle":"Do’s & Don’ts", "bgimage":"4", "lesimage":"l4"]
]
and
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! LessonsTableViewCell
let lessonsObject = mylessons[indexPath.row]
cell.backgroundImageView.image = UIImage(named: lessonsObject["bgimage"]!)
cell.titleLabel.text = lessonsObject["title"]
cell.subtitleLabal.text = lessonsObject["subtitle"]
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
performSegue(withIdentifier: "LessonSegue", sender: mylessons[indexPath.row])
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
let lessegue = segue.destination as! LessonDetailsViewController
lessegue.SelectedLessons = mylessons
}
LessonDetailsViewController:
#IBOutlet weak var LTitle: UILabel!
var SelectedLessons = [Dictionary<String, String>()]
override func viewDidLoad() {
super.viewDidLoad()
LTitle.text = SelectedLessons["title"]
// Do any additional setup after loading the view.
}
Finally, it has an error "Cannot subscript a value of type '[Dictionary]' with an index of type 'String'.
First your SelectedLessons is wrong type. You need use something like tis
var SelectedLessons:Dictionary<String, String>?
And you need past correct object.
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
let lessegue = segue.destination as! LessonDetailsViewController
lessegue.SelectedLessons = sender as? Dictionary<String,String>
}
You should declare
var SelectedLessons = [String, String]()
Your current declaration is an array of dictionaries
You have a number of problems.
First is a coding style issue. Variable names should start with a lower-case letter, so SelectedLessons should be selectedLessons.
Second, you likely want to pass the user-selected lesson to the destination, not the entire array.
Your array mylessons is an array of dictionaries: (Type [[String:String]])
You should probably name the variable in LessonDetailsViewController selectedLesson (singular, starting with a lower-case letter) and make it type [String: String] (a single lesson.)
Then your prepareForSegue might look like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
guard
let lessegue = segue.destination as? LessonDetailsViewController,
let selectedRow = tableView.indexPathForSelectedRow?.row else {
print("Error. No row selected. Exiting."
fatalError()
}
lessegue.selectedLesson = myLessons[selectedRow]
}
(The code above should have better error handling for the case where there's not a selected row, but it should give you the idea.)
EDIT:
By the way, it's not a good idea to write your prepare(for:) method as if you will only ever segue to a single view controller of a single type. It's very common to go back and expand an app to add additional segues, and if you do that, the code above will crash. Better to use a switch statement:
override func prepare(for segue: UIStoryboardSegue, sender: Any?){
switch segue.destination {
case let lessegue as LessonDetailsViewController:
guard
let selectedRow = tableView.indexPathForSelectedRow?.row else {
print("Error. No row selected. Exiting."
fatalError()
}
lessegue.selectedLesson = myLessons[selectedRow]
default:
print("Unrecognized segue. Exiting."
fatalError()
}
}
That syntax creates a switch statement where each case is executed based on the type of the destination view controller, with a built-in cast to the destination type. It's a neat variant of the switch statement that's very useful in prepare(for:) functions.

Delay or error performing segue from TableView

I have seen two similar questions here but none of their answers really helped me.
I have a tableView of comments and I want to perform a segue to a detail of the comment (kind of like twitter does, if you click on a tweet you have a detail view of it). However the information given to the detail view is the penultimate row selected and not the last selected. And if you only select one, the segue wont be even performed.
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("detail_segue", sender: indexPath)
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "detail_segue"){
let row = (sender as! NSIndexPath).row;
let commentForSegue = self.AOS[row]
let destinationVC = segue.destinationViewController as! CommentDetailVC
destinationVC.detail_title = commentForSegue.titulo_comment
destinationVC.detail_body = commentForSegue.cuerpo_comment
destinationVC.detail_date = commentForSegue.fecha_comment
destinationVC.detail_num_agree = String(commentForSegue.num_agrees)
destinationVC.detail_num_disagree = String(commentForSegue.num_disagrees)
destinationVC.detail_agreed = commentForSegue.agreed
}
}
I've tried with and without the dispatch_async both on the prepareForSegue and didSelectRowAtIndexPath but it doesnt work. I've also tried doing all the work from the didSelectRowAtIndexPath but no success either.
Thanks!!
First of all, you need to call the segue in the method didSelectRowAtIndexPath and you're calling it from the method didDeselectRowAtIndexPath exist a little difference between both, but are some tips to get the last cell tapped too, see the following code:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
// the identifier of the segue is the same you set in the Attributes Inspector
self.performSegueWithIdentifier("detail_segue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "detail_segue"){
// this is the way of get the indexPath for the selected cell
let indexPath = self.tableView.indexPathForSelectedRow()
let row = indexPath.row
let commentForSegue = self.AOS[row]
let destinationVC = segue.destinationViewController as! CommentDetailVC
destinationVC.detail_title = commentForSegue.titulo_comment
destinationVC.detail_body = commentForSegue.cuerpo_comment
destinationVC.detail_date = commentForSegue.fecha_comment
destinationVC.detail_num_agree = String(commentForSegue.num_agrees)
destinationVC.detail_num_disagree = String(commentForSegue.num_disagrees)
destinationVC.detail_agreed = commentForSegue.agreed
}
}
I hope this help you.

Resources