Can selecting UICollectionViewCell and getting indexPath be done by this way? - ios

I am having a scenario in which I need to get the indexpath of selected cell & have the contents of an array at that particular index number of a collection view. I can do that by various ways. But I wonder that if there is an equivalent code for collection view as I have did for table view in the following code -
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?)
{
if segue.identifier == "segueupdate"
{
let update = segue.destinationViewController as! EnterDetailViewController
let indexpath = self.tableview .indexPathForSelectedRow
let row = indexpath?.row
print(people[row!])//here it prints the value at particular index of the array
}
}
The advantage of this method I feel is that I don't need to write the didSelectRowAtIndexPath(). So Can I do something like this for collection view in which I need not write the didSelectItemAtIndexPath() ? My Code so far with error commented at the particular line -
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let indexPath = self.myCollectionView.indexPathsForSelectedItems()
let selectedIndexPath = indexPath?.first
print(selectedIndexPath)
print( people.objectAtIndex((indexPath?.row)!) //Here I get an error as value of [NSIndexPath] has no member row?
}

There is indexPathsForSelectedItems which will return an NSArray of NSIndexPath objects or nil depending on what has been selected in your collectionView.
To fix what you have tried do this
let selectedIndexPaths = self.myCollectionView!.indexPathsForSelectedItems()
let selectedIndexPath = selectedIndexPaths?.first
print(selectedIndexPath)
print(people[(selectedIndexPath?.row)!])
However if you just want to print something from your array for the selected cell I would do
override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath)
{
print(people[indexPath.row])
}

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!]

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.

how can I move to another view controller?

I have a problem with indexPathForSelectedRow()
I have 2 viewcontroller:
tableview and view controller
I want to move from tableview to another controller
and I used func prepareForSegue but I have problem in indexpathselectedrow()
This is issue for the problem:
cannot invoke "indexpathforselectedRow" with no arguments
The Xcode is a last version
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "show"
{
let indexpath = tableView.indexPathForSelectedRow()
let detailvc: fiveViewController = segue.destinationViewController as! fiveViewController
}
}
There is the indexPathForSelectedRow definition in Swift 1.2:
- (NSIndexPath *)indexPathForSelectedRow; // returns nil or index path representing section and row of selection.
There is its definition in Swift 2.0:
var indexPathForSelectedRow: NSIndexPath? { get } // returns nil or index path representing section and row of selection.
In Swift 2.0, the indexPathForSelectedRow is a property not a method, so you should change:
let indexpath = tableView.indexPathForSelectedRow()
to
let indexpath = tableView.indexPathForSelectedRow
Sounds like there is no reference to tableView or it's not of type UITableView.
override func prepareForSegue(segue: UIStoryboardSegue , sender: AnyObject?) {
if segue.identifier == "show"
{
let indexpath = self.tableView.indexPathsForSelectedRows()!
let detailv:fiveViewController = segue.destinationViewController as! fiveViewController
detailv.pic = self.menu[indexpath.row].picName
}
this link for the pic " my problem "

Passing value to another ViewController upon selecting cell in UICollectionView

I want to select a username from a UICollectionView, and pass that string to another view.
I can select the cell using "didSelectRowAtIndexPath". However the prepareForSegue function runs before my selection is set, and the segue passes the initial value.
I can't figure out how to get the index value in the prepareForSegue overide. If it were a UITableView, I think I could use indexPathForSelectedRow.
Here's what I have:
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedUsername = userNames[indexPath.row]
selectedRow = indexPath.row
println ("Cell \(indexPath.row) selected")
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "userSelectSegue") {
let nextVC = segue.destinationViewController as ViewController;
nextVC.passedUsername = userNames[selectedRow]
println("prepareForSegue")
}
}
Running this always passes userNames[0]
The output window Prints:
prepareForSegue
Cell 2 selected
EDIT 1:
I basically think I want the following tableview code, but I can't get it working for UICollectionView (i.e. I can't get the cell number):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "userSelectSegue") {
let nextVC = segue.destinationViewController as ViewController;
let indexPath = UserSelectCollection.indexPathForSelectedRow()
nextVC.passedUsername = userNames[indexPath.row]
}
}
Here's How I solved it:
I deleted the segue connecting the collection cell to the second view controller
I created another segue between the two view controllers (called the identifier "userSelectSegue")
Added a performSegueWithIdentifier call to the didSelectItemAtIndexPath
Here's the code:
var userNames = ["Bob", "Sally", "Mike", "Mary", "James"]
var selectedRow = 0
func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
self.performSegueWithIdentifier("userSelectSegue", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "userSelectSegue") {
let nextVC = segue.destinationViewController as ViewController;
nextVC.passedUsername = userNames[selectedRow]
}
}
This is the general behavior of the iOS. What i implement is in this case is. Do not override the method didSelectRowAtIndexPath. When you tap on any cell, then in the function prepareForSegue. You can get the cell in the sender object. From that cell get the selected item. Get the indexPath.item. After that get the object from the array and pass it to the next view controller.

Push segue from UITableViewCell to ViewController in Swift

I'm encountering problems with my UITableViewCells. I connected my UITableView to a API to populate my cells.
Then I've created a function which grabs the indexPath.row to identify which JSON-object inside the array that should be sent to the RestaurantViewController.
Link to my Xcode Project for easier debugging and problem-solving
Here's how my small snippet looks for setting the "row-clicks" to a global variable.
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
i = indexPath.row
}
And here's my prepareForSegue() function that should hook up my push-segue to the RestaurantViewController.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let navigationController = segue.destinationViewController as UINavigationController
let vc = navigationController.topViewController as RestaurantViewController
vc.data = currentResponse[i] as NSArray
}
}
And here's how I've set up my segue from the UITableViewCell
Here's my result, I've tried to click every single one of these cells but I won't be pushed to another viewController...I also don't get an error. What is wrong here?
Tried solutions that won't work
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let vc = segue.destinationViewController as RestaurantViewController
//let vc = navigationController.topViewController as RestaurantViewController
vc.data = currentResponse[i] as NSArray
}
}
The problem is that you're not handling your data correctly.
If you look into your currentResponse Array, you'll see that it holds NSDictionaries but in your prepareForSegue you try to cast a NSDictionary to a NSArray, which will make the app crash.
Change the data variable in RestaurantViewController to a NSDictionary and change your prepareForSegue to pass a a NSDictionary
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = redditListTableView.indexPathForCell(cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destinationViewController as RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
For Swift 5
func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = self.tableView.indexPath(for: cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destination as! RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
The following steps should fix your problem. If not, please let me know.
Remove your tableView(tableView, didSelectRowAtIndexPath:) implementation.
Make data on RestaurantViewController have type NSDictionary!
Determine the selected row in prepareForSegue:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let cell = sender as? UITableViewCell {
let i = tableView.indexPathForCell(cell)!.row
if segue.identifier == "toRestaurant" {
let vc = segue.destinationViewController as RestaurantViewController
vc.data = currentResponse[i] as NSDictionary
}
}
}
Dropbox link to stack3 directory
I am having difficulty understanding why your software is much different than a standard 2 level tableview structure. So I coded a short example which you can access from this link. I have also included the sources code below.
The program mimics what you have (as best as I understood it). Table Controller 1 segues to Table Controller 2 from the tableview cell. I had no issues with segue-ing. Notice that I do not have nor need to augment the Storybook to initiate the segue.
I have embedded both the controllers in Navigation Controllers. My experience is that it saves a lot of effort to set up the navigation.
Alternately, I could have control-dragged from the first TableViewController symbol on top of the screen to the second controller and set up the segue.
I used a global variable (selectedRow) although it is not a recommend practice. But you just as easily use the prepareForSegue to set a variable in the RestaurantTableViewController (I show an example)
Finally, I recommend checking the Connections Inspector (for the table view cell in the first controller) to confirm that there is a segue to the second controller. If you control-dragged properly there should be confirmation prompt as well as an entry in the Connections Inspector.
Unfortunately I just cant get the code properly formatter
import UIKit
var selectedRow = -1
class TableViewController: UITableViewController {
var firstArray = ["Item1","Item2","Item3","Item4"]
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return firstArray.count
}
let nameOfCell = "RestaurantCell"
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(nameOfCell, forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = firstArray[indexPath.row]
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
// MARK: - Navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let vc = segue.destinationViewController as RestaurantTableViewController
// can write to variables in RestaurantTableViewController if required
vc.someVariable = selectedRow
}
}
import UIKit
class RestaurantTableViewController: UITableViewController {
var secondArray = ["Item 2.1", "Item 2.2", "Item 2.3", "Item 2.4"]
var someVariable = -1
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return secondArray.count
}
let nameOfCell = "RestaurantCell"
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(nameOfCell, forIndexPath: indexPath) as UITableViewCell
cell.textLabel!.text = secondArray[indexPath.row]
if indexPath.row == selectedRow {
cell.textLabel!.text = cell.textLabel!.text! + " SELECTED"
}
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
}
I noticed that in your screenshot of your storyboard, the segue is connecting the first prototype cell to the RestaurantViewController. This prototype cell looks like it's the "Basic" style of cell with a disclosure indicator accessory on the right. But look at the screenshot of your app running. The table is being populated with cells that appear to be the "Subtitle" style of cell without a disclosure indicator accessory on the right.
The reason that your segue is never firing no matter what you do is that the segue is only configured to work for a specific prototype cell, but that prototype cell is never being used when you populate the table. Whatever you're doing in tableView:cellForRowAtIndexPath:, you're not using the prototype cell that you want.
#Starscream has the right idea dequeueing the right cell with the right identifier and matching it with the identifier of the prototype cell in Interface Builder. The crash that you're getting even after doing that might be because of the previous problem mentioned in the comments above. Your segue in the storyboard is clearly pointing to a UITableViewController. Your code in prepareForSegue:sender: should be let vc = segue.destinationViewController as RestaurantViewController, as long as RestaurantViewController is a subclass of UITableViewController. You'll crash if you try to cast it as a UINavigationController. Also make sure that the class for the destination UITableViewController in the storyboard is listed as RestaurantController in the Identity Inspector pane. You'll crash if your program compiles thinking that the storyboard just contains a generic UITableViewController there.
Getting back to the original problem more, I don't know how you've implemented tableView:cellForRowAtIndexPath:, which might be crucial. Maybe it's not so simple. Maybe you plan on handling many prototype cells or generate custom cells at runtime. In this case, one way to make this simple for you is to programmatically perform the segue when the user taps on a cell. Instead of using a specific prototype cell, make the segue a connection originating from the "Restauranger nära mig" UITableViewController going to the RestaurantViewController. (Connect in Interface Builder by control-click dragging from the Table View Controller icon at the top of the first one over to the body of the second). You must give this segue an identifier in the Attributes Inspector pane to make this useful. Let's say it's "toRestaurant". Then at the end of your tableView:didSelectRowAtIndexPath: method, put this line of code: self.performSegueWithIdentifier("toRestaurant", sender: self). Now no matter what cell is selected in the table, this segue will always fire for you.
Try creating cells like this in your cellForRow method:
let cell: UITableViewCell = tableView.dequeueReusableCellWithIdentifier("MyTestCell", forIndexPath: indexPath)
Im going out on a whim here since I am just getting into swift right now but the way I do it in my prepareForSegue() is something like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "toRestaurant"{
let navigationController = segue.destinationViewController as UINavigationController
let vc = navigationController.topViewController as RestaurantViewController
//notice I changed [i] to [index!.row]
vc.data = currentResponse[index!.row] as NSArray
}
}
What it looks like to me is that you are calling the i variable which is kind of like a private variable inside a method of your class. You can do something like #Syed Tariq did with the selectRow variable and set it above your class SomeController: UIViewController /*, maybe some more here? */ { and then sign the variable inside your
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
selectedRow = indexPath.row
}
method like above but both ways should work rather well.
I had the same problem and I found the solution to be:
performSegueWithIdentifier("toViewDetails", sender: self)
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
var cellnumber = procMgr.processos[indexPath.row].numero
println("You selected cell #\(indexPath.row)")
println(cellnumber)
performSegueWithIdentifier("toViewDetails", sender: self)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "toViewDetails" {
let DestViewController : ViewDetails = segue.destinationViewController as! ViewDetails
}
}
You may need to get the selected cell index of the UItableview. Below code used the selected cell index (UItableview.indexPathForSelectedRow) to get a correct element of the array.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "seguaVisitCardDetial" {
let viewController = segue.destinationViewController as! VCVisitCardDetial
viewController.dataThisCard = self.listOfVisitCards[(tblCardList.indexPathForSelectedRow?.row)!]
}
}
I had this problem, too; the segue from UITableViewCell did not call.
After some searching, I found it is because I had chosen "No Selection" for "Selection" field.

Resources