I am not sure i understood it correctly, the documentation is a bit confusing here, but given the following code in the body of a UITableView delegate:
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
let action = UITableViewRowAction(style: UITableViewRowAction.Style.normal,
title: "Do Something!") { (_, indexPath) in
self.doSomething()
}
return [action]
}
the call to doSomething() method is performed in simulator on completion of the swipe-to-left action beside on tap on the "Do Something!" button.
I don't want the call to be performed twice. Is there something wrong with my configuration or i have not understood the purpose of UITableViewRowAction?
TLDR: i want swipe action callback to be triggered only when the button that appears is tapped.
Thank you.
To prevent this behavior you need an implementation of new iOS.
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let myAction = UIContextualAction(style: .someStyle, title: "Some tilte") { (action, sourceView, completionHandler) in
print("action has been triggered")
completionHandler(true)
}
let preventSwipeFullAction = UISwipeActionsConfiguration(actions: [myAction ])
preventSwipeFullAction .performsFirstActionWithFullSwipe = false // set false to disable full swipe action
return preventSwipeFullAction
}
TableView Trailing Swipe Action
Remember tableView.isEditing need to be false to allow trailingSwipeActionsConfigurationForRowAt be called.
completionHandler(true) // passing true enable handler call action
Related
I have swipe to delete code here and it my custom TableViewCell I have implemented setSelected method like below ..
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
// tableView.allowsSelectionDuringEditing = false
if tableView.indexPathForSelectedRow != nil, tableView.indexPathForSelectedRow == indexPath {
return UITableViewCell.EditingStyle.none
}
return UITableViewCell.EditingStyle.delete
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
//some code here
}
The logic will do tableview expand collapse based on selection ..but the problem here is if I swipe to delete setSelected also triggers.. not sure how to prevent that any help would be appreciated..
Try this in cellForRow
let cell = tableView.dequeueReusableCell(withIdentifier: "cell_identifier", for: indexPath)
cell.selectionStyle = UITableViewCell.SelectionStyle.none
//or this based on swift version
cell.selectionStyle = .none
return cell
I'm not sure what the appearance you are going for but this will produce the same effect.
If swipe is across the whole screen it will trigger the delete without button press.
If half swipe you can present buttons for user to choose options.
Half swipe (Shows options):
Full swipe (Triggers delete button):
Try adding these two tableView delegate methods for a swipe to delete
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
// Determine which rows should be editable
return true
}
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath:
IndexPath) -> [UITableViewRowAction]? {
let button1 = UITableViewRowAction(style: .default, title: "Delete") {
action, indexPath in
print("delete pressed")
// Consider alert to confirm that it was intentionally deleted
}
button1.backgroundColor = UIColor.red
// Create any buttons you want
return [button1]
}
I tried it with and without your tableView method and it seemed to work fine both ways but I don't think your method is necessary if you choose this route
Hope this helps
When my tableView goes into editing mode there's an icon on each side of my cell: the icon on the right is the default icon for rearranging the order of cells, and the icon on the left is the default icon for deleting a cell. I can easily interact with the rearranging icon with no problems, however, tapping on the delete icon gives no response. It only works when I do an awkward right swipe then left swipe on it. Below I've placed what I believe to be all the relevant code to my situation.
Some background information: I have three touch gestures in the view (a single tap, double tap, and long press). I've tried disabling them with conditions for when the editing mode is enabled. I've even tried deleting the touch gestures beforehand, but that didn't help. I thought it might be an auto layout issue that's not registering the area for the delete button, but the rearranging button works so shouldn't this? Both the my cell's leading and trailing constants are +10 to the view. Furthermore, isEditing is controlled by a UIButton. Finally, i have the tableView delegate set to self, and touch interaction when editing is enabled.
Any help would be appreciated. Thank you!
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
if isEditing { return true }
return false
}
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
if isEditing { return .delete }
return .none
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == .delete) {
countBeforeDeletingCell = dataSource.data.count
dataSource.data.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .none)
}
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let movedObject = dataSource.data[sourceIndexPath.row]
dataSource.data.remove(at: sourceIndexPath.row)
dataSource.data.insert(movedObject, at: destinationIndexPath.row)
tableView.reloadData()
}
Hey If the you have use UITableViewController and using isEditing iOS defalut mode you have to override setEditing Mode and Changing Edit button Click.
iOS self.navigationItem.rightBarButtonItem = self.editButtonItem in editButtonItem is default UIViewController method. editButtonItem button Action override.
Swift Example:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
tableView.reloadData()
}
it works for me on swift 5.7.May be you should config TrailingSwipeActionConfiguration like this:
unc tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let deleteAction = UIContextualAction(style: .destructive,
title: "Delete") { [weak self] _, _, complete in
//remove row in you data model here
tableView.deleteRows(at: [indexPath], with: .automatic)
complete(true)
}
deleteAction.backgroundColor = .red
return UISwipeActionsConfiguration(actions: [deleteAction])
}
I just implemented trailingSwipeActionsConfigurationForRowAt and leadingSwipeActionsConfigurationForRowAt to add swipe actions to my UITableViewCells.
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration
func tableView(_ tableView: UITableView, leadingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration
This triggers (on my UITableViewController) func setEditing(_ editing: Bool, animated: Bool) to be called with editing = true, which in turn also triggers my editing animation and other changes that I want when going into edit mode. (The contextual actions are not related to editing/deleting).
I don't want this, but have yet to find a way to disable this behaviour, even just detecting that setEditing is called via the swipe actions.
Any ideas?
I was able to solve this by triggering the "real" edit mode differently.
On my UITableViewController:
class MyTableViewController: UITableViewController {
var realEditMode: Bool = false
func setRealEditing(_ editing: Bool) {
realEditMode = editing
setEditing(realEditMode, animated: true)
}
// See Note 1 below
#available(iOS 11.0, *)
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration {
guard let item = itemForIndexPath(indexPath) else {
return UISwipeActionsConfiguration(actions: [])
}
if realEditMode {
return UISwipeActionsConfiguration(actions: [
buildActionConfiguration(.delete, fromIndexPath: indexPath)
])
} else {
return UISwipeActionsConfiguration(actions: [
buildActionConfiguration(.read, fromIndexPath: indexPath)
])
}
}
#available(iOS 11.0, *)
func buildActionConfiguration(_ action: MyCellActionEnum, fromIndexPath indexPath: IndexPath) -> UIContextualAction {
// logic to build `UIContextualAction`
}
}
And in my UITableViewCell check if the editing flag is set by manual triggering or by swipe edit triggering:
class MyCell: UITableViewCell {
var myTableViewController: MyTableViewController?
override func setEditing(_ editing: Bool, animated: Bool) {
if editing && !(myTableViewController?.realEditMode ?? true) {
return
}
super.setEditing(editing, animated: animated)
}
}
Then on the edit buttons in the UI I changed setEditing(true/false, animated: true) to setRealEditing(true/false) instead.
Note 1
One problem I found was that when using trailingSwipeActionsConfigurationForRowAt was that the delete button (⛔️) didn't work anymore. Tapping it did not trigger the confirmation swipe.
I found that there had to exist a trailingSwipeActionsConfigurationForRowAt with a UIContextualAction that was initialized with UIContextualAction(style: .destructive) (i.e had destructive style). This is the item that is then used for displaying the delete confirmation.
However I did not want that item to be visible when the regular swipe actions were used, so to only show it one "real edit mode" I used the realEditMode flag.
This worked for me and didn't seem too hacky. If anything more official pops up I'm more than happy to change the accepted answer.
I have a UITableViewCell where I have implemented leadingSwipeActionsConfigurationForRowAt indexPath to allow users to drag right on cells to add them to their favorites. However, in doing so the option for dragging left to delete also appeared. I don't want these cells to be able to be deleted. Is there a way to implement swipe actions without the delete action appearing?
Try this
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return false
}
Edit
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let swipeAction = UISwipeActionsConfiguration(actions: [])
swipeAction.performsFirstActionWithFullSwipe = false // This is the line which disables full swipe
return swipeAction
}
I have a
tableView(UITableView)
customCell: UITableViewCell!
itemsArray: [Item]
I tried to set custom action for cell in UITableView depending on IndexPath.row.
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath)
-> [UITableViewRowAction]? {
if !(itemsArray[indexPath.row].isReadOnly) {
let editAction = UITableViewRowAction(style: .normal, title: "Edit") {
(tableViewRowAction, indexPath) in
print("Edit Item \(self.itemsArray[indexPath.row].caption)\n")
}
return [editAction]
} else {
return []
}
}
I tried to set custom action for cell in UITableView depending on IndexPath.row:
The problem occurs with cells, which I want to be without actions (which corresponding items has .isReadOnly = true)
I tried to return nil and [] in else case and both variants has irrelevant result for swiping:
- nil – Delete action shows for items
- [] – Cell swipes a little bit, “unswipes” back an than swipes stops working in any cell
I found a solution: just use canEditRowAt method of UITableView
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return !(items[indexPath.row].isReadOnly)
}