I have UITableViewController and after user pres desired button, I have "full edit" mode. To enable this, I call this:
#objc func btnEditPressed(sender: UIButton) {
sender.isSelected = !self.isEditing
self.setEditing(!self.isEditing, animated: true)
self.tableView.beginUpdates()
self.tableView.endUpdates()
}
for each UITableViewCell, I have override func setEditing(_ editing: Bool, animated: Bool) method. In this, I hide some parts of cell in "full edit" mode.
Now, when the user swipe single table row, I show the delete button. However, in this "delete row mode" I dont want to hide any information from the cell. The problem is, that the same override func setEditing(_ editing: Bool, animated: Bool) for the cell is called as in the first case, leading to hiding cell content.
Is there an easy way, how to solve this, or I have to keep weak reference to UITableViewController and check the mode from the cell myself?
You have two options to handle this.
Check showingDeleteConfirmation in the cell's setEditing method.
Override willTransition(to:) and didTransition(to:) in your cell class.
If your table view is not in editing mode and the user performs a swipe-to-delete gesture, then your cell will experience the following sequence:
willTransition(to:) will be called with the mask including the value UITableViewCell.StateMask.showingDeleteConfirmation
setEditing will be called with editing set to true. The cell's showingDeleteConfirmation will be equal to true
didTransition(to:) will be called with the mask including the value UITableViewCell.StateMask.showingDeleteConfirmation
So the simplest solution is to update your cell's setEditing:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if showingDeleteConfirmation {
// User did a swipe-to-delete
} else {
// The table view is in full edit mode
}
}
Related
I have a UITableView with swipe action that when swiping: A) displays a blank/empty area where the swipe action button should display; and B) the following line is logged to the debug console in Xcode:
[Assert] The pullView is not in a view hierarchy. This is a UIKit bug.
As of today, zero Google results exist for searching on the title of this question.
It turns out that the culprit was the following
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if !editing {
rowSelectionState.removeAll()
loadData()
}
refreshView()
}
Specifically, the refreshView() call, which contains a tableView.reloadData() call, needs to be inside of the if !editing { ... } block. If not, when a swipe action is initiated, the swipe action appears to call setEditMode(true, ...), thus calling tableView.reloadData() which messes with the UISwipeActionsConfiguration's ability to be properly displayed.
Thus the above should look like this:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if !editing {
rowSelectionState.removeAll()
loadData()
refreshView()
}
}
Can someone explain to me why the methods setSelected(_:animated:)
and setHighlighted(_:animated:) are called when a TableView is initialized/loaded?
I would have assumed the methods are ONLY called when I actually highlight/select a cell...
I tested this in a TableViewController with three dynamic, custom cells, where I simply print out some text in the method calls:
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
print("setSelected()")
}
override func setHighlighted(_ highlighted: Bool, animated: Bool) {
print("setHighlighted()")
}
Console output:
setSelected()
setHighlighted()
setSelected()
setHighlighted()
setSelected()
setHighlighted()
setSelected(_:animated:) and setHighlighted(_:animated:) are called every time when a cell in the TableView is initiated. Because the setSelected method is used to set the isSelected property when a cell is selected or not. By default every cell is selected false. To set selected = false, this method is called.
setHighlighted is called every time a cell is initiated. Because every cell is initiated with the isHighLighted property is set to false. To set this value this method is called by default. Whether you set true or false manually or not.
As per the discussion for method in the documentation:
Parameters:
selected:
true to set the cell as selected, false to set it as unselected. The default is false.
animated:
true to animate the transition between selected states, false to make the transition immediate.
So in unselected case that during the loading state, it will be false. If you want to perform any action in case of selection ONLY, add a check for if selected to avoid repetition.
I am implementing navigationItem.leftBarButtonItem = editButtonItem under viewDidLoad(), and it is said that I have to implement setEditing(_ editing: Bool, animated: Bool) as well. It seems like every editing functionality works great without setEditing function. What does it do??
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.leftBarButtonItem = editButtonItem
tableView.allowsMultipleSelectionDuringEditing = true
}
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: true)
tableView.setEditing(tableView.isEditing, animated: true)
}
and it is said that I have to implement setEditing(_ editing: Bool, animated: Bool) as well
Then "it is said" incorrectly.
The built-in editButtonItem of a UITableViewController automatically calls the table view's setEditing for you; there is no need, therefore, to duplicate that functionality. To be more precise:
The built-in editButtonItem of a UIViewController does two things:
It calls the UIViewController's setEditing(_:animated:) when tapped.
It tracks the UIViewController's isEditing property, and changes its own title accordingly (Edit or Done).
Moreover, UITableViewController's implementation of setEditing(_:animated:) calls setEditing(_:animated:) on its table view.
Thus, you would need to do that last step if this were not a UITableViewController. But it is, so you don't.
I am using a Edit/Done button to move an MKMapView upwards when Edit mode is selected, with the intention of display a message in an imageView at the bottom of the screen. My understanding is to change the function of this button I must use override func setEditing().
While I can get it to change from Edit to Done mode once, with the corresponding title change, I can never get it to change back to Edit mode. The result being when you just press "Done" over and over it remains "Done" and keeps moving the MKMapView up.
I want this to operate as a toggle but the process escapes me for some reason:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: true)
if editing {
print("Are we editing NOW: \(isEditing)")
mapView.frame.origin.y = -24
editButtonItem.title = "Done"
} else {
print("Are we editing: \(isEditing)")
mapView.frame.origin.y = 64
editButtonItem.title = "Edit"
}
super.setEditing(editing, animated: true)
My attempted implementations of "isEditing" and "isEnabled" within my if/else statement haven't worked so far. What am I missing?
EDIT:
Well I came up with this approach, which works but seems clunky.
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: true)
if editing {
print("Are we editing NOW: \(isEditing)")
mapView.frame.origin.y = -24
editButtonItem.title = "Done"
} else {
print("Are we editing: \(isEditing)")
mapView.frame.origin.y = 64
editButtonItem.title = "Edit"
}
super.setEditing(editing, animated: true)
}
I am having to use 64 as a value to get the mapView to return to the bottom the super view, not exactly sure why.
First, do not create your own button for the Edit/Done button. UIViewController provides this for you using the editButtonItem property.
I don't know how to use it in a storyboard but in code you add the following line in your viewDidLoad method:
navigationItem.rightBarButtonItem = editButtonItem
This button is already setup to call the setEditing(_:animated:) method and to toggle the button title between Edit and Done.
So all you need to do is to override the setEditing(_:animated:) method.
Your implementation is close. Only call super.setEditing once at the beginning and don't try to set the button's title.
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: true)
if editing {
print("Are we editing NOW: \(isEditing)")
mapView.frame.origin.y = -24
} else {
print("Are we editing: \(isEditing)")
mapView.frame.origin.y = 64
}
}
In some cases I want my tableview in editmode when the view appears. To do this I set editMode = true when segue to the view and call setEditing in viewWillAppear method. When the view is in editing mode and the view is pushed to another view and popped back to this view, the table is not editable anymore. Could someone tell me how to stay in editing mode or do this a better way?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(true)
self.setEditing(self.editMode, animated: false)
}
It was a simple fix..
override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if (editing){
print("Editing")
self.editMode = true
}
else {
print("Done editing")
self.editMode = false
}
}
TableViews have their own properties to track whether it is in edit mode or not:
yourTable.isEditing
So no need to add "editMode" to the table and manually set it whenever the table goes in or out of editing mode.