Error when Removing Row from TableView (Swift) - ios

I want to delete a row from a table view, but it isn't working :/
This next code block is triggered from a custom button (not from an internal Apple-Table-Edit).
self.tableView.beginUpdates()
self.sections[indexPath.section].items.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .automatic)
self.tableView.endUpdates()
This is how a section looks like:
class Section: Comparable {
var state: TrackingObject.stateEnum
var items: [TrackingObject]
var expanded: Bool
init(state : TrackingObject.stateEnum, items : [TrackingObject], expanded : Bool){
self.state = state
self.items = items
self.expanded = expanded
}
static func group(trackingObjects: [TrackingObject]) -> [Section] {
let groups = Dictionary(grouping: Model.trackingObjects) { (trackingObject: TrackingObject) -> TrackingObject.stateEnum in
return trackingObject.currentState
}
return groups.map { (state: TrackingObject.stateEnum, trackingObjects: [TrackingObject]) in
return Section(state: state, items: trackingObjects, expanded: state == .active_due ? true : false)
}.sorted()
}
}
Expected: That the row of the given indexPath is removed with an animation as well as removed in the 'data source' (called: self.sections)
Actual Output: Error-Message:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'attempt to insert row 2
into section 0, but there are only 0 rows in section 0 after the
update'
(I was removing the second row in the section 0)

self.sections[indexPath.section].items.remove(at: indexPath.row)
self.tableView.beginUpdates()
DispatchQueue.main.async {
tableView.deleteRows(at: [indexPath], with: .automatic)
self.tableView.endUpdates()
}
Try removing item from your data source before beginUpdates(). You can also just remove item from the data source and reload the table view as you are setting the animation option as automatic, so tableview will perform the preferable animation.

Related

Tableview deleting rows and sections

I've implemented the following deleting mechanism: on delete action tableView deletes selected rows and if section does not have any rows it will be deleted.
My mechanism looks like this:
guard let selectedRows = self.tableView.indexPathsForSelectedRows,
let indexPath = self.tableView.indexPathForSelectedRow else { return }
self.tableView.beginUpdates()
self.dataSource[indexPath.section].rows.remove(at: indexPath.item)
if self.dataSource[indexPath.section].rows.isEmpty {
self.dataSource.remove(at: indexPath.item)
self.tableView.deleteSections(IndexSet(arrayLiteral: indexPath.section), with: .fade)
}
self.tableView.deleteRows(at: selectedRows, with: .fade)
self.tableView.endUpdates()
My dataSource object:
struct DataSource {
var section:String
var rows:[Row]
}
struct Row {
var data:String
}
The problem is when I select more than one row and try to delete selected rows I've got a crush with following message:
Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (5) must be equal to the number of rows contained in that section before the update (6), plus or minus the number of rows inserted or deleted from that section (0 inserted, 2 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
While I delete by one row my mechanism works fine, as expected. Even deletes an empty section. Any thoughts, where am I wrong? Thanks a lot.
Solution: just add inverted indexPath array. let invertedIndexPathes = selectedRows.sorted(by: >)
Fixed mechanism:
guard let selectedRows = self.tableView.indexPathsForSelectedRows,
let indexPath = self.tableView.indexPathForSelectedRow else { return }
let invertedIndexPathes = selectedRows.sorted(by: >)
self.tableView.beginUpdates()
for itemIndexPath in invertedIndexPathes {
self.dataSource[itemIndexPath.section].rows.remove(at: itemIndexPath.row)
self.tableView.deleteRows(at: [itemIndexPath], with: .fade)
self.tableView.reloadData()
}
if self.dataSource[indexPath.section].rows.isEmpty {
self.dataSource.remove(at: indexPath.item)
self.tableView.deleteSections(IndexSet(arrayLiteral: indexPath.section), with: .fade)
}
self.tableView.endUpdates()

How to reload the last cell in a section in swift

I am trying to load a cell into a certain section of my tableView with an animation, but It is crashing giving me this error 'NSInternalInconsistencyException', reason: 'attempt to delete row 1 from section 0 which only contains 1 rows before the update'
from this code:
let indexPath = IndexPath(item: session[section].count - 1, section: section)
tableView.reloadRows(at: [indexPath], with: .automatic)
this code is triggered after a user clicks a button to add another cell to the end of that section. All I want to do is add that cell to the end of the section with an animation, how can I do that?
#objc func addSetPressed(_ sender: UIButton){
let section = sender.tag
session[section].append(ExerciseSet(setID: "", sessionID: "", exerciseID: "", setIndex: section + 1, weight: session[section][0].weight, reps: session[section][0].reps))
//tableView.reloadData()
let indexPath = IndexPath(item: session[section].count - 1, section: section)
tableView.reloadRows(at: [indexPath], with: .automatic)
setTableViewHeight()
UIImpactFeedbackGenerator(style: .soft).impactOccurred()
}

Swift: Is it possible to not reload the first cell/row when using tableView.reloadData()?

I was wondering if it is posible to reload a tableView without reloading the first cell/row in a simple way? If so, how would you do it?
let itemArray:[Int] = []
for i in 1..<itemArray.count {
let indexPath = IndexPath(item: i, section: 0)
tableView.reloadRows(at: [indexPath], with: .fade)
}
If your table is not grouped currently ,
The best way to do this is add your 1st cell to one section And all other cells to another section. Then you can easily reload all cell excluding 1st cell using
//section 0 - your 1st cell
//section 1 - other cells
tableView.reloadSections([1], with: .none)
Just use the reloadRows method to reload your tableView.
var dataSource: [SomeData] = []
func reloadAllButFirstRow() {
guard dataSource.count > 0 else { return }
tableView.reloadRows(
at: (1...dataSource.count).map { IndexPath(row: $0, section: 0) },
with: .fade
)
}

How to delete all rows of section in Swift

I am working on a code where I have to insert & delete all rows on selecting a section.
Below is the code for inserting & deleting
var paths = [IndexPath]()
for row in 0..<(arrayOfSection?.count)! {
let indexPath = IndexPath(row: row, section: selectedIndex!)
paths.append(indexPath)
}
// Toggle collapse
if previousSelectedIndex != nil && previousSelectedIndex == section {
self.arrayOfCategories?.removeAll()
self.catergoryTableView.beginUpdates()
self.catergoryTableView.deleteRows(at: paths, with: .fade)
self.catergoryTableView.endUpdates()
}
else{
self.catergoryTableView.beginUpdates()
self.catergoryTableView.insertRows(at: paths, with: .fade)
self.catergoryTableView.endUpdates()
header.setCollapsed(false)
//scroll
let sectionRect = self.catergoryTableView.rect(forSection: selectedIndex!)
self.catergoryTableView.scrollRectToVisible(sectionRect, animated: true)
}
previousSelectedIndex = selectedIndex
Inserting rows works fine but when I am trying to delete rows from selected section in am getting below error
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'attempt to delete row 9 from section 0 which only contains 0 rows before the update'
What am I doing wrong?
Update your code to delete all row as follows:
self.catergoryTableView.beginUpdates()
self.catergoryTableView.deleteRows(at: paths, with: .fade)
self.catergoryTableView.endUpdates()
self.arrayOfCategories?.removeAll()
Just add first line of code to the last.

Swift3 collectionView 'attempt to delete item 1 from section 1, but there are only 1 sections before the update'

My app crashed due to 'attempt to delete item 1 from section 1, but there are only 1 sections before the update'
I found other articles said that the data source must keep in sync with the collectionView or tableView, so I remove the object from my array: alarms before I delete the item in my collectionView, this works on didSelectItemAtIndexPath. But I don't know why this doesn't work.
I also tried to print the array.count, it showed the object did remove form the array.
func disableAlarmAfterReceiving(notification: UILocalNotification) {
for alarm in alarms {
if alarm == notification.fireDate {
if let index = alarms.index(of: alarm) {
let indexPath = NSIndexPath(item: index, section: 1)
alarms.remove(at: index)
collectionView.deleteItems(at: [indexPath as IndexPath])
reloadCell()
}
}
}
}
func reloadCell() {
userDefaults.set(alarms, forKey: "alarms")
DispatchQueue.main.async {
self.collectionView.reloadData()
print("Cell reloaded")
print(self.alarms.count)
}
}
You are having single section so you need to delete it from the 0 section. Also use Swift 3 native IndexPath directly instead of NSIndexPath.
let indexPath = IndexPath(item: index, section: 0)
alarms.remove(at: index)
DispatchQueue.main.async {
collectionView.deleteItems(at: [indexPath])
}
Edit: Try to break the loop after deleting items from collectionView.
if let index = alarms.index(of: alarm) {
let indexPath = IndexPath(item: index, section: 0)
alarms.remove(at: index)
DispatchQueue.main.async {
collectionView.deleteItems(at: [indexPath])
}
reloadCell()
break
}

Resources