0

I noticed in that to disable the highlight feature of a table cell you would yse the following method:

override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
  ...
  tableView.deselectRowAtIndexPath(indexPath, animated: false)
}

What I don't understand is what's going on in the first argument indexPath. In the instructional book I've been reading just about every other method has been using indexPath.row for selecting individual cells:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cellIdentifier = "Cell"
        let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as CustomTableViewCell

        cell.nameLabel.text = restaurantNames[indexPath.row]
        cell.typeLabel.text = restaurantTypes[indexPath.row]
        cell.locationLabel.text = restaurantLocations[indexPath.row]
}

Just out of curiosity I tried passing indexPath.row as an argument but got the error, 'NSNumber' is not a subtype of 'NSIndexPath'. In this particular case what is the main difference between using indexPath and indexPath.row.

Carl Edwards
  • 13,826
  • 11
  • 57
  • 119
  • indexPath is a combination of the section and row. if you only pass row then it will return error. to deselect you need both but to get data from array you only need to use row if you are not using section. if you will use more then one section then getting value from array with indexPath.row will also be wrong. But in your case its fine. – Mahesh Agrawal Mar 08 '15 at 14:35

5 Answers5

2

From the NSIndexPath class reference:

The NSIndexPath class represents the path to a specific node in a tree of nested array collections. This path is known as an index path.

Table views (and collection views) use NSIndexPath to index their items because they are nested structures (sections and rows). The first index is the section of the tableview and the second index the row within a section.

indexPath.section and indexPath.row are just a "convenience" properties, you always have

indexPath.section == indexPath.indexAtPosition(0)
indexPath.row     == indexPath.indexAtPosition(1)

They are documented in NSIndexPath UIKit Additions.

So NSIndexPath is an Objective-C class and is used by all table view functions. The section and row property are NSInteger numbers. (Therefore you cannot pass indexPath.row if an NSIndexPath is expected.)

For a table view without sections (UITableViewStyle.Plain) the section is always zero, and indexPath.row is the row number of an item (in the one and only section). In that case you can use indexPath.row to index an array acting as table view data source:

cell.nameLabel.text = restaurantNames[indexPath.row]

For a table view with sections (UITableViewStyle.Grouped) you have to use both indexPath.section and indexPath.row (as in this example: What would be a good data structure for UITableView in grouped mode).


A better way to disable the selection of particular table view rows is to override the willSelectRowAtIndexPath delegate method:

override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {

    // return `indexPath` if selecting the row is permitted
    // return `nil` otherwise
}

In addition, to disable the appearance of the cell highlight on touch-down, you can also set

cell.selectionStyle = .None

when creating the cell. All this is (for example) discussed in UITableview: How to Disable Selection for Some Rows but Not Others.

Community
  • 1
  • 1
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thanks for the explanation. So when `tableView.deselectRowAtIndexPath(indexPath, animated: false)` is called is it in a sense looking for any selected row within that particular indexPath and deselecting it? – Carl Edwards Mar 08 '15 at 15:11
  • @CarlEdwards: No, `indexPath` (which has a `row` and a `section` property) fully and uniquely identifies the item in the table view. – Martin R Mar 08 '15 at 15:21
  • @CarlEdwards: I have also added an alternative solution. – Martin R Mar 08 '15 at 15:38
  • @Downvoter: Some explaining comment would be helpful. Please let me (and other readers) know if anything is wrong here, so that I can fix it. – Martin R Mar 08 '15 at 16:47
1

Your question and confusion stems from the fact that Apple decided to use somewhat imprecise method name/signature

tableView.deselectRowAtIndexPath(indexPath, animated: false)

The "Row" word in the method signature implies that row is going to be deselected, therefore one would expect a row as a method parameter. But a table view can have more than one section. But, in case it doesn't have more sections, the whole table view is considered one section. So to deselect a row (a cell really) in table view, we need to tell both which section and which row (even in case of having only one section). And this is bundled together in the indexPath object you pass as a parameter.

So the before-mentioned method might instead have a signature

tableView.deselectCellAtIndexPath(indexPath, animated: false)

which would be more consistent, or another option, albeit a bit less semantically solid (in terms of parameters matching the signature)

tableView.deselectRowInSectionAtIndexPath(indexPath, animated: false)

Now the other case - when you implement the delegate method

tableview:cellForRowAtIndexPath

here again (Apple, Apple...) the signature is not completely precise. They could have chose a more consistent method signature, for example

 tableview:cellForIndexPath: 

but Apple decided to stress the fact that we are dealing with rows. This is an 8 year old API that was never the most shining work of Apple in UIKit, so it's understandable. I don't have statistical data, but I believe having a single section (the whole tableview is one section) is the dominant approach in apps that use a table view, so you don't deal with the sections explicitly, and work only with the row value inside the delegate method. The row often serves as index to retrieve some model object or other data to be put into cell. Nevertheless the section value is still there. You can check this anytime, the passed indexPath will show the section value for you when you inspect it.

Earl Grey
  • 7,426
  • 6
  • 39
  • 59
0

NSIndexPath is an object comprised of two NSIntegers. It's original use was for paths to nodes in a tree, where the two integers represented indexes and length. But a different (and perhaps more frequent) use for this object is to refer to elements of a UITable. In that situation, there are two NSIntegers in each NSIndexPath: indexPath.row and indexPath.section. These conveniently identify the section of a UITableView and it's row. (Absent from the original definition of NSIndexPath, row and section are category extensions to it).

Check out tables with multiple sections and you'll see why an NSIndexPath (with two dimensions) is a more general way to reference a UITableView element than just a one-dimensional row number.

jbbenni
  • 1,158
  • 12
  • 31
0

i see the perfect answer is from apple doc

https://developer.apple.com/documentation/uikit/uitableview/1614881-indexpath

if you print indexpath you may get [0,0] which is first item in the first section

Ahmed Medhat
  • 85
  • 1
  • 3
0

I was confused between indexPath and indexPath.row until i understand that the concept is the same for the TableView and the CollectionView.

indexPath           :  [0, 2]
indexPath.section   :    0
indexPath.row       :    2

//

indexPath           :  the whole object (array)
indexPath.section   :  refers to the section (first item in the array)
indexPath.row       :  refers to the selected item in the section (second item in the array)

Sometimes, you may need to pass the whole object (indexPath).

Essam Fahmi
  • 1,920
  • 24
  • 31