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
Related
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 want even numbered rows of tableview to be selected when table is first loaded. I have set the tableview editing style to "Multiple Selection During Editing" from storyboard. I have tried two ways to do this, first approach is like below
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.setEditing(true, animated: true)
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")
cell?.textLabel?.text = data[indexPath.row]
if (indexPath.row % 2 == 0){
cell?.isSelected = true
}else {
cell?.isSelected = false
}
return cell!
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
but it does not make the row selected. table looks like
second way i tried is to call self.tableView.selectRow(at: index path, animated: true, scrollPosition: UITableViewScrollPosition.none) whith removing additional logic in cellForRow method used in first approach. this approach seems to work.
Can anyone tell me why first approach is not working and weather my second approach is appropriate or not. Thanks :)
Check if you are doing deselectRowAtIndexPath in didSelectRowAtIndexPath method.
So I have been looking all over the internet for an answer to this but to no avail. Basically, I have code to have cells in a tableview have a check mark when selected, which works fine. However, when selected, I want the indexPath to be saved in a user default so that the next time the user views it, that particular cell that she had selected before can be pre-selected:
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .checkmark
UserDefaults.standard.set(indexPath as IndexPath, forKey: "OnHomeShow")
}
}
override func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
if let cell = tableView.cellForRow(at: indexPath) {
cell.accessoryType = .none
}
}
The saving in the user default didn't work! Anyone able to help? Furthermore, how do I get it so that I can have a pre-selected cell? I used this code here:
override func viewDidAppear(_ animated: Bool) {
if let x = UserDefaults.standard.object(forKey: "OnHomeShow") as? IndexPath
{
//I want to pre-select the cell set by the user-default
}
else
{
//I want to pre-select the first cell
}
}
But I have no idea what code to put to select a cell programmatically! Anyone know how to help? Thanks!
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)
}