I'm trying to implement a simple TableView with Realm DataSource.
First, I create a ChangeSet
cars = realm.objects(CarModel.self)
carChangeset = Observable.changeset(from: cars, synchronousStart: false).observeOn(MainScheduler.instance)
and I created an instance of RxTableViewRealmDataSource
let dataSource = RxTableViewRealmDataSource<CarModel>(cellIdentifier: "Cell", cellType: CarListTableViewCell.self) { (cell, ip, element) in
cell.viewModel = CarDetailViewModel(car: element)
}
Then, I bound my changeset to the tableview
carChangeset
.bind(to: tableView.rx.realmChanges(dataSource))
.disposed(by: bag)
I run my project, but, no matter how I swipe a cell in my TableView, the "delete" button will never appear.
After looking for a solution to my problem, I read that, in order to a TableView to present the "red delete" button when swiping, the DataSource delegate must implement tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath). As RxTableViewRealmDataSource is not declared open on the original distribution, and I don't want to modify the sources, I created an extension that implements this function:
extension RxTableViewRealmDataSource where E: CarModel {
public func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Should Delete car")
}
}
public func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
}
Still nothing. My TableView does not react to the swipe gesture.
Is this a bug on the RxRealm/RxRealmDataSource implementation? Am I doing something wrong? Or do I missed something?
Thanks in advance,
Related
I am trying enable editing of my UITableView by providing an Edit button on the nav bar.
The intention is to allow the user to insert new items and delete existing items.
When clicking the Edit button iOS shows the "Delete" decoration (deleting works fine), but doesnt show "Insert"
I have added navigationItem.rightBarButtonItem = editButtonItem to my UIViewControllers viewDidLoad and have added the following methods to my UITableViewDataSource delegate:
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
AppDelegate.persistenceContext.delete(self.fetchedResultsController.object(at: indexPath))
AppDelegate.saveContext()
} else if editingStyle == .insert {
let current = self.fetchedResultsController.object(at: indexPath)
// do rest of insertion logic
}
}
What am I missing something?
Oh and out of interest, if this is possible would the new cell be inserted above or below the one on which the action was initiated?
For the record I can add custom swipe actions to the cells, but the users feedback is they prefer an edit button.
Any light would be appreciated, all the Googling has gotten me is using a button to insert a new row, or adding custom swipe actions.
You need to implement the delegate method:
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle
Return .delete, .insert, or .none for a given index path. Normally only one row (typically first or last) would return .insert and the rest would return .delete. Return .none for any row that can't be inserted or deleted.
It's not uncommon that you also need to override the setEditing(_ editing: Bool, animated: Bool) method to add/remove an extra row just for the purposes of showing the green + (.insert) icon.
An alternative is to show the + icon in the nav bar instead of having a row with the insert icon.
How do I remove a static table view cell by doing a if statement like this:
if ____ {
remove static cell
} else {
keep cell in tableview
}
The bolded part is what I need the code for. I searched the internet for an answer, but I could not find one. Thanks for the help! I'm using Swift 3
First, make sure to change cells to have Dynamic Properties, because static cells are hard-coded.
Second, you don't need an else statement. If the condition is true, delete the cell, otherwise do nothing. To delete a cell, use a function:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
print("Cell deleted")
// delete any additional data from containers like arrays or dictionaries if needed
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
If this is a static tableview you can't remove a cell. If you attempt to you'll probably fall into all sorts of issues. Your best solution is to set the cell's height to zero and hide it.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = super.tableView(tableView, cellForRowAt:indexPath)
if indexPath == cellToHide {
cell.isHidden = true
}
return cell
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
if indexPath == cellToHide {
return 0
}
return super.tableView(tableView, heightForRowAt: indexPath)
}
I've been using editActionsForRowAt to create the built-in swipe left functionality to reveal quick-access buttons, like this:
func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {
print("Is this working?")
//Code to create UITableViewRowAction buttons
return [button1, button2]
}
func tableView (_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
//I read I need this, so I have it
return true
}
func tableView (_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
//I read I need this, so I have it
}
Now my whole table, including the swipe, works great. Until...
My editActionsForRowAt function has code where if the user swipes a cell that's expanded (by default they're 50, but users can tap to expand the height) then editActionsForRowAt returns [], an empty array. I do that because I don't want really tall cells showing stretched out buttons.
After that, even after closing, swiping a cell (any cell) doesn't even call editActionsForRowAt. I checked by adding a print-line right at the beginning of that function and it doesn't print no matter what I do after swiping an expanded cell and returning [].
Any ideas on how to fix this?
Instead of returning an empty array from editActionsForRowAt, you should return false from canEditRowAt when a given row isn't editable.
I added a custom swipe to share action on my UITableViewCells in my UITableView. My problem is that for some reason it won't even let me swipe the cell. It immediately crashes with this line:
reason: 'attempt to delete row 25 from section 0 which only contains 25 rows before the update'
However, I'm not trying to delete anything! It's a share button not a delete button. Here are the relevant methods I've added:
override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {
let shareAction = UITableViewRowAction(style: .Normal, title: "Share") { (action: UITableViewRowAction, indexPath: NSIndexPath) -> Void in
// Some stuff
}
shareAction.backgroundColor = UIColor.yellowColor()
return [shareAction]
}
override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
return true
}
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
}
Why is my table view controller giving me this error when it should have no business attempting to delete anything?
It turns out it was an issue unrelated to the three methods above. My table view controller is a PFQueryTableViewController subclass which has numberOfRowsInSection baked in out of the box. My problem was that I explicitly added that method when I shouldn't have. Therefore it was being called when it shouldn't have been (it's build into the subclass implicitly), resulting in some wonky behavior. I removed my call to numberOfRowsInSection and it worked as it should.
I am trying to learn Swift, but there is a problem in my project that drives me nuts.
I have a working list of data in a ViewController fed by parse.com. I managed to implement a swipe-feature that reveals buttons for both deleting and editing. That is working fine. Now I want the user to be able to reorder the cells. So I successfully implemented a button to put the table into editing-mode. My 2 problems with that are:
When I enter edit-mode I just want to be able to reorder the cells since editing and deleting is done via swipe (via "tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath)". How can I achive that the user doesn't see the 2 buttons for deleting and editing when in editing-mode and touching the delete-circle that is provided automatically?
Is it possible to remove the delete-circle altogether? Using "UITableViewCellEditingStyle.None" also disables the swipe-functionality.
Thanks in advance!
To avoid the round red delete button that appears when you put set UITableView isEditing to true at the left and does nothing when you click it, the minimum that worked for me was this (Swift 4, iOS 11)
// Avoid the round red delete button on the left of the cell:
func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
I also have these functions, which probably interact:
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return savedTempTable.isEditing
}
// Including this function in the delegate enable left-swipe deleting
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath)
{
if editingStyle == .delete {
savedConversions.remove(at: indexPath.row)
}
}
// Including this function enables reordering
func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath,to destinationIndexPath: IndexPath)
{
let elem = savedConversions.remove(at: sourceIndexPath.row)
savedConversions.insert(elem, at: destinationIndexPath.row)
}
override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
return .none
}
override func tableView(_ tableView: UITableView, shouldIndentWhileEditingRowAt indexPath: IndexPath) -> Bool {
return false
}
Although people can delete a row through swipe the delete-button in editing mode should not be removed. People may not know about the swipe gesture and by removing the delete button (which they already expect in editing mode) the app becomes more difficult to use.
If you really want to remove the delete button then you have to implement the delegate method tableView(_:editingStyleForRowAtIndexPath:). There you can return .None while the screen is in editing mode and .Delete while the screen is not.
To enable reordering you have to implement the data source methods tableView(_:canMoveRowAtIndexPath:) and tableView(_:moveRowAtIndexPath:toIndexPath:).
You follow this way to remove the delete Icon while editing:
-(UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath{
return UITableViewCellAccessoryNone;
}