I have a table view with a certain number of rows. With a button on the same ViewController, I want to pop out one of the tableView rows. However, I am unable to do so. Note that I am not using editing style of table view but using a button on the same ViewController to delete a row from the tableView.
func postAction() {
postTable.deleteRows(at: [IndexPath(row: 0, section: 0)] , with: .fade)
}
However this results in crash. I am not sure why is this happening and how I should correct this. I intend to delete the first row from the only section here in the tableView (postTable)
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 0.
The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
My code:
postTable.delegate = self
postTable.dataSource = self
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 3
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = JobPostCellView(style: UITableViewCellStyle.default , reuseIdentifier: "PostCell")
cell.delegate = self
cell.name = arr[indexPath.row]
return cell
}
try this
func postAction() {
let indexPath = IndexPath(row: 0, section: 0)
yourmodel.remove(at: indexPath.row)//update your model also here
postTable.deleteRows(at: [indexPath] , with: .fade)
}
this may be happenig because you tableview row is not matching with the returning rows from number of rows in section method of your data source
for example
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10
}
if you are returning 10 in your numberOfRowsInSection then there will be 10 rows in table
now you are deleting 1 row from it so the rows will be 9 but your numberOfRowsInSection method still returning 10 so this will create inconsistency so you have to update your count in numberOfRowsInSection section to return 9
and if you are using dynamic array than you have to delete it from your array also like in answer
Related
I am playing with a table as below (I've seen multiple threads on this topic but couldn't answer my question):
func numberOfSections(in _: UITableView) -> Int {
3
}
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return 3
} else if section == 1 {
return 4
} else {
return 2
}
}
Then after clicking on a cell I want a new cell inserted into the same section at row index 0. I wrote the below:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.insertRows(at: [IndexPath(row: 0, section: indexPath.section)], with: .left)
}
I get this error:
Exception NSException * "Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (3) must be equal to the number of rows contained in that section before the update (3), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out)."
I assume I should somehow update the data source, but since in my case I return const values for rows in sections is there a way to update the the number of rows in section? (the numberOfRows in section only returns immutable value). Thank you
in my case I return const values
There you go! If you are going to modify the number of rows you need variables
For example
var sections = [3,4,2]
func numberOfSections(in _: UITableView) -> Int {
return sections.count
}
func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section]
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
sections[indexPath.section] += 1
tableView.insertRows(at: [IndexPath(row: 0, section: indexPath.section)], with: .left)
}
I have a table view with two sections
I added the swipe to delete row
but the app crash cause there's error select the current indexPath
I tried two different ways but none of this works
//the code
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let contextItem = UIContextualAction(style: .destructive, title: "Delete") { (contextualAction, view, boolValue) in
switch indexPath.section{
case 0:
//1 i tried this
self.tableView.deleteRows(at: [indexPath], with: .automatic)
case 1:
//2 and i tried this
self.tableView.deleteRows(at: [IndexPath(row: indexPath.row, section: 1)], with: .automatic)
default:break
}
boolValue(true)
}
let swipeActions = UISwipeActionsConfiguration(actions: [contextItem])
return swipeActions
}
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
the result says: "Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Invalid update: invalid number of rows in section 1. 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 (5), plus or minus the number of rows inserted or deleted from that section (0 inserted, 1 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'"
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of rows in section 1. 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 (5), plus or minus
the number of rows inserted or deleted from that section (0 inserted,
1 deleted) and plus or minus the number of rows moved into or out of
that section (0 moved in, 0 moved out).'
It says the exact thing you gotta do. Remember when deleting data, you need to make your DATASOURCE count equal to the rows and section count after the method deleteRows or deleteSections.
Which means you manipulate your data source array before calling those methods. And when using multi-sections, remember to carefully access your datasource by both section and row, and also when deleting and inserting data.
func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let contextItem = UIContextualAction(style: .destructive, title: "Delete") { (contextualAction, view, boolValue) in
let section = indexPath.section
let row = indexPath.row
self.data[section].remove(at: row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
boolValue(true)
}
let swipeActions = UISwipeActionsConfiguration(actions: [contextItem])
return swipeActions
}
I have an array:
var users = [Users]()
And in editActionsForRowAt I do following:
self.viewModel.users.remove(at: indexPath.row)
self.usersTableView.deleteRows(at: [indexPath], with: .automatic)
and it crashes on deleteRows with the next error:
attempt to delete row 0 from section 0 which only
contains 0 rows before the update
I tried to debug and found the next: when I remove a user from the array:
self.viewModel.users.remove(at: indexPath.row)
it removes somehow the row also! Like before this line
self.usersTableView.numberOfRows(inSection: 0)
gives me 1. After deletion from the array BUT before deleteRows it becomes equal to0`. I cannot understand the problem. It's so weird. Can someone help me to understand it?
UPDATE
numberOfRowsInSection:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.viewModel.users.count
}
My data has 50 entries. The simulator stops at 12. I don't understand.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
funcSectCount += 1
let data = AppData.orgNames
print("data.count=", data.count, " func Section count=", funcSectCount)
return data.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
funcRowCount += 1
let data = AppData.orgNames
let cell = myTable.dequeueReusableCell(withIdentifier: "myCell", for:
indexPath)
cell.textLabel?.text = data[indexPath.row]
print("indexPath.row=", indexPath.row, " func Row count=", funcRowCount)
return cell
}
This as the final print for each function:
data.count= 50 func Section count= 3 (curious, but minor)
indexPath.row= 11 func Row count= 12
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) is called by the table view each time it needs a new cell.
If only 12 cells are visible at a time then the table view initially needs only 12 cells so will ask for only 12 cells. You'd have to scroll before it would need to ask for more. It won't request cells until it needs them.
So interestingly, even though the table is displaying correctly, the printout only reaches 12, and that happens regardless of how many cells you scroll to. Is that what you are finding? This is because you have 12 rows in a view, and the cells are reused, so you are not creating more cells but you are just reusing the 12 that you already have.
I get a NSInternalInconsistencyException exception. It says : 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (1) must be equal to the number of rows contained in that section before the update (1), plus or minus the number of rows inserted or deleted from that section (1 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).
It says before the update numberOfRowsInSection was 1, but it is not how I can see during debugging. Before the update, last time when numberOfRowsInSection was called it returned 0.
Do you have any idea?
this is what I see:
numberOfRowsInSection return 0
self.tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
numberOfRowsInSection get called and return 1
crash
my solution has two things: (1) add beginUpdates / endUpdates (2) add a bool variable to check whether view was already loaded or not
more explanation: if beginUpdates triggers a view reload cycle then viewcontroller will note that datasource already increased, so no insertRowsAtIndexPaths needs, if beginUpdates not triggers view reload cycle then neigter numberOfRowsInSection will be called so insertRowsAtIndexPaths can be called
func controller(controller: NSFetchedResultsController!, didChangeObject anObject: AnyObject!, atIndexPath indexPath: NSIndexPath!, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath!) {
if controller == frc {
switch(type) {
case .Insert:
isLayoutReloaded = false
self.tableView.beginUpdates() //<- !!!
if !isLayoutReloaded { //<- !!!
self.tableView.insertRowsAtIndexPaths([newIndexPath], withRowAnimation: .Fade)
}
self.tableView.endUpdates() //<- !!!
}
}
}
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int {
isLayoutReloaded = true //<- !!!
let numberOfRowsInSection = ((frc!.sections as NSArray)[0] as NSFetchedResultsSectionInfo).numberOfObjects
return numberOfRowsInSection
}