4

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?

Dave G
  • 12,042
  • 7
  • 57
  • 83

3 Answers3

1

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.

AskIOS
  • 918
  • 7
  • 19
1

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]
}
beyowulf
  • 15,101
  • 2
  • 34
  • 40
  • But here's the thing about manually adding a button to act like an accessory... the programmatic adding of an accessory is great! It sizes it right without stretching and vertically centers too. Adding an accessory even adjusts the body of the cell. Most important for me, in some case the table should load with no accessories at all so it's nice having it be an easy on-off. There's no way to add an accessory image view and make it tappable? – Dave G Jan 22 '16 at 00:47
  • You can set the button to be the accessoryView of the cell but you need to set the target in the same manner as I described above. AccessoryView is a read write UIView property on UITableViewCell. You should know that if you set accessoryView is set accessoryType is ignored, hence if you want to use UITableViewCellAccessoryCheckmark in the action of the button you'd have to reload the cell and set accessoryView to nil and accessoryType to UITableViewCellAccessoryCheckmark and there would be no way for the user to reverse that action as UITableViewCellAccessoryCheckmark does not respond to taps – beyowulf Jan 22 '16 at 01:39
0

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.

Dave G
  • 12,042
  • 7
  • 57
  • 83