[swift-users] Issues with UITableView*

Ole Begemann ole at oleb.net
Mon Sep 12 17:41:35 CDT 2016

 > Hmm - interesting to know.  Unfortunately, if I do that, then I get
 > NO indication of selection when I click on ANY row.  Perhaps I need
 > to make some other changes to account for the change in how I get the
 > cell?

You also need to store your cells' selection state someplace outside of 
the cells themselves. The cells should not be the "source of truth" for 
the selection state. Otherwise, when you scroll a cell off screen and 
then scroll it back, it will lose its state.

So you should store the selection state of each table row somewhere 
alongside your `locationList` array. Maybe as an array of pairs like this:

     var locationList: [(location: LocationObject, selected: Bool)] = [
             location: LocationObject(name: "name-1", address: "addr-1", 
phone: "phone-1", latitude: 40.0, longitude: -80.1),
             selected: false
             location: LocationObject(name: "name-2", address: "addr-2", 
phone: "phone-2", latitude: 40.0, longitude: -80.1),
             selected: false

There may be better ways to structure your model data, but this should 
suffice for now.


1. In `tableView(_:cellForRowAtIndexPath:)`, use the current value of 
`item.selected` to configure the cell's selection state:

     override func tableView(tableView: UITableView, 
cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
         let cell = 
tableView.dequeueReusableCellWithIdentifier("resultCell", forIndexPath: 
indexPath) as! ExistingLocationTableViewCell
         let item = locationList[indexPath.row]

         cell.nameLabel.text     = item.location.name
         cell.locationLabel.text = item.location.address

         cell.accessoryType = item.selected ? .Checkmark : .None

         return cell

One note: on iOS, the convention for table views is that rows should 
generally not remain selected after the user lifts their finger. Adding 
the checkmark should be enough to show a cell's selection state. I would 
only set the checkmark and leave `cell.selected` as is (I left it out in 
the code above).

2. In `didSelectRow...`, toggle the selection state in your model data:

     override func tableView(tableView: UITableView, 
didSelectRowAtIndexPath indexPath: NSIndexPath) {

         // Update model
         let row = indexPath.row
         locationList[row].selected = !locationList[row].selected

To update the UI, you now have two choices. Either ask the table view 
for the cell at the index path and modify the cell directly:

         // Either do this:
         if let cell = tableView.cellForRowAtIndexPath(indexPath) {
             cell.accessoryType = locationList[row].selected ? 
.Checkmark : .None

If you do that, I don't think you need to reload the cell explicitly. 
Alternatively, tell the table view to reload the cell as you are doing 
now. It will then call `tableView(_:cellForRowAtIndexPath:)` again, 
which in turn will configure the cell with your model data:

         // Or this:
         tableView.reloadRowsAtIndexPaths([indexPath], withRowAnimation: 

Finally, fade out the cell selection:

         tableView.deselectRowAtIndexPath(indexPath, animated: true)

3. If you are okay with keeping the cells deselected unless the user's 
finger is onscreen, you don't need to implement `didDeselectRow...` at all.

(I typed this mostly without help from the compiler as I don't have a 
Swift 2.x handy, so there may be some errors in the code above.)


More information about the swift-users mailing list