问题
I have a TableView with cells that when pressed anywhere in the cell, it adds a checkmark on the right. I only want the checkmark to show up if the cell is tapped on the right side. Here's the pertinent section of code from the TableViewController:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
let task = tasks[indexPath.row]
cell.task = task
if task.completed {
cell.accessoryType = UITableViewCellAccessoryType.checkmark;
} else {
cell.accessoryType = UITableViewCellAccessoryType.none;
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
var tappedItem = tasks[indexPath.row] as Task
tappedItem.completed = !tappedItem.completed
tasks[indexPath.row] = tappedItem
tableView.reloadRows(at: [indexPath], with: UITableViewRowAnimation.none)
}
}
Is there a simple way to do that, or to do it using storyboard? My Swift skills leave a LOT to be desired. Any help would be appreciated! Thank you!
回答1:
Instead of the built-in checkmark accessory type, why not provide, as accessory view, an actual button that the user can tap and that can display the checkmark? The button might, for example, display as an empty circle normally and as a circle with a checkmark in it when the user taps it.
Otherwise, you're expecting the user to guess at an obscure interface, whereas, this way, it's perfectly obvious that you tap here to mark the task as done.
Example:
To accomplish that, I created a button subclass and set the accessoryView
of each cell to an instance of it:
class CheckButton : UIButton {
convenience init() {
self.init(frame:CGRect.init(x: 0, y: 0, width: 20, height: 20))
self.layer.borderWidth = 2
self.layer.cornerRadius = 10
self.titleLabel?.font = UIFont(name:"Georgia", size:10)
self.setTitleColor(.black, for: .normal)
self.check(false)
}
func check(_ yn:Bool) {
self.setTitle(yn ? "✔" : "", for: .normal)
}
override init(frame:CGRect) {
super.init(frame:frame)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The title of the button can be the empty string or a checkmark character, thus giving the effect you see when the button is tapped. This code comes from cellForRowAt:
:
if cell.accessoryView == nil {
let cb = CheckButton()
cb.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
cell.accessoryView = cb
}
let cb = cell.accessoryView as! CheckButton
cb.check(self.rowChecked[indexPath.row])
(where rowChecked
is an array of Bool).
回答2:
You will have to define your own accessory button, and handle its own clicks.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TaskCell", for: indexPath) as! TaskCell
let task = tasks[indexPath.row]
cell.task = task
let checkButton = UIButtonSubclass()
...configure button with your circle and check images and a 'selected property'...
checkButton.addTarget(self, action:#selector(buttonTapped(_:forEvent:)), for: .touchUpInside)
cell.accessoryView = checkButton
checkButton.selected = task.completed //... this should toggle its state...
return cell
}
func buttonTapped(_ target:UIButton, forEvent event: UIEvent) {
guard let touch = event.allTouches?.first else { return }
let point = touch.location(in: self.tableview)
let indexPath = self.tableview.indexPathForRow(at: point)
if let task = tasks[indexPath.row] {
task.completed = !task.completed
}
tableView.reloadData() //could also just reload the row you tapped
}
Though, it has been noted that using tags to detect which row was tapped is dangerous if you start to delete rows. You can read more here https://stackoverflow.com/a/9274863/1189470
EDITTED Removed the reference to tags per @matt
来源:https://stackoverflow.com/questions/43035765/want-accessory-checkmark-to-show-only-when-tapped-on-the-right