3
votes

I have a UITableView with custom cells. Usually when the user taps the cell, it triggers this function:

func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { 
     //Segue to another view
}

If in that view they mark the cell as finished, when they return the code will add the standard check accessory.

When the cell isn't finished, I'd like an empty check that can be tapped (I know I can add a custom image accessory), with the tap allowing the user to skip the segue and quickly mark the cell as finished. But I can't seem to make both work:

  1. Tapping on the main body of the cell should call didSelectRowAtIndexPath
  2. Tapping on the accessory view (empty check mark) should call a different set of code.

I've tried accessoryButtonTappedForRowWithIndexPath but it doesn't seem to even get called.

func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) { 
     //Shortcut code to change the cell to "finished"
}

Is it possible to have clicking on the main body trigger one set of code and clicking on the accessory view trigger another? If so, how?

3

3 Answers

1
votes

Its a Objective-C solution. When you have added your own accessoryButton, that won't call tableviewDelegate method of 'accessoryButtonTappedForRowWithIndexPath'. What you should do is, create a UIButton and addTarget to that method, then add it as a accessoryButton on tableViewCell. Also set tag value to button as index path.row so that you get to know which row is clicked.

1
votes

You should add a UIButton to your custom UITableViewCell. You can then add a target for that button called pressedFinished, by saying something like cell.button.addTarget(self, action: "finishedPress:", forControlEvents: .TouchUpInside) or something then in pressedFinished you can say something like:

func pressedFinished(sender:UIButton)
{
   let location = self.tableView.convertPoint(sender.bounds.origin, fromView: sender)
   let indexPath = self.tableView.indexPathForRowAtPoint(location)
   //update your model to reflect task at indexPath is finished and reloadData
}

It's generally not a great practice to use tags as they have no inherent meaning. Also tags mapped to indexPath.row will only work if the table has one section.

Another option might be using UITableViewRowAction:

override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath:      NSIndexPath) -> [AnyObject]? {
  let rowAction : UITableViewRowAction
  if model[indexPath].finished
  {
      rowAction = UITableViewRowAction(style: .Normal, title: "Mark as Unfinished", handler: {(rowAction:UITableViewRowAction,indexPath:NSIndexPath)->() in
      //mark as unfinished in model and reload cell
      })
 }else{
      rowAction = UITableViewRowAction(style: .Normal, title: "Mark as Finished", handler: {(rowAction:UITableViewRowAction,indexPath:NSIndexPath)->() in
      //mark as finished in model and reload cell
      })
  }
  return [rowAction]
}
0
votes

I selected the answer that I thought gave a thorough answer, and which helped me out. However, I deviated from that answer a bit and wanted to share this as well:

Making The Accessory View Appear / Disappear

I load several lists into the same storyboard table. Sometimes the list should show indicators, other times no accessory is necessary. To do this, I added a button in the storyboard, centered vertically and trailing = 0 to the right container view. Then I gave it a width of 0 and gave that constraint an IBOutlet in my code. When I want the accessory, I simply give it a width. To make it disappear, I set its width back to 0.

Accessories and Core Data

Accessories are kind of a pain because if the user checks something off then closes the app, they expect it to be remembered. So for every change, you need to save that cells state in CoreData. I added an attribute called "state" and give it a value of "selected" when the accessory should show up filled.

This also means that I have to sort by that attribute when retrieving the list. If you already have a sort descriptor, you'll now need several.