How to append row in tableview swift? - ios

I'm adding data in my model and model is assigned to tableview to reload data. But every time reloading is not looking good. so I want just last element that was added in model, should be appended in already exist tableview. Tried so many ways but getting crash when my tableview is empty.
let lastSectionIndex = self.isGroupChat ? self.objGroupChatList!.count-1 : self.objSingleChatList!.count-1
var lastRow = 0
if self.isGroupChat {
lastRow = (self.objGroupChatList?[lastSectionIndex].count ?? 1)
} else {
lastRow = (self.objSingleChatList?[lastSectionIndex].count ?? 1)
}
let IndexPathOfLastRow = IndexPath(row: lastRow-1, section: lastSectionIndex)
self.tableView.beginUpdates()
self.tableView.insertRows(at: [IndexPathOfLastRow], with: UITableViewRowAnimation.none)
self.tableView.endUpdates()
This is crashing with error:
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'Invalid update: invalid
number of sections. The number of sections contained in the table
view after the update (1) must be equal to the number of sections
contained in the table view before the update (0), plus or minus the
number of sections inserted or deleted (0 inserted, 0 deleted).'

You should use insertSections for new sections. insertRows only works for existing sections.

You need to do something like,
let section = 0 //get your section here...
dataSource[section].append("five")
let row = dataSource[section].count - 1
tableView.insertRows(at: [IndexPath(row: row, section: section)], with: .none)
This is just an example of how you can get that working. Fill the gaps as per your code.

Related

How can I avoid reloading the whole table, disrupting the scroll

I wanted to cap the number of elements to three. When I add element four, element one should disappear.
If I want scrolling to continue fine, I should tell UITableView that I am deleting rows in the entire section zero by calling deleteRowsAtIndexPaths. After that I need to tell UITableView that you are inserting a bunch of rows at the third section (section index 2).
This way you would be able to avoid reloading the whole table, disrupting the scroll.
But unfortunately it is not working for me.
Give me this error :
'attempt to delete row 3 from section 2, but there are only 1 sections before the update'
code :
//scrolling down time calling this method
func oldestState(){
//var students : [[Student]]? = [[],[],[]]
let data = getdata()
self.students?[(self.firstIndex+self.count) % (self.students?.count)!] = data
if (self.count != 3) {
self.count += 1
} else {
self.firstIndex = (self.firstIndex+1) % (self.students?.count)!
}
self.newsFeedTableView.beginUpdates()
let indexPathsDeleted = (0..<(data
.count)).map { IndexPath(row: $0, section: (self.students?.count)! - 1) }
self.newsFeedTableView.deleteSections([0], with: UITableViewRowAnimation.automatic)
self.newsFeedTableView.deleteRows(at: indexPathsDeleted, with: UITableViewRowAnimation.automatic)
self.newsFeedTableView.endUpdates()
self.newsFeedTableView.beginUpdates()
let indexPathsInsert = (0..<(data
.count)).map { IndexPath(row: $0, section: (self.students?.count)! - 1) }
self.newsFeedTableView.insertSections([2], with: UITableViewRowAnimation.automatic)
self.newsFeedTableView.insertRows(at: indexPathsInsert, with: UITableViewRowAnimation.automatic)
self.newsFeedTableView.endUpdates()
}
func getdata() -> [Student]{
var _students = [Student]()
for i in itemNumber..<(itemNumber + 4) {
let student = Student()
student.name = "\(i)"
print("adding student roll number : \(student.name)")
_students.append(student)
}
itemNumber += 4
return _students
}
}
Here is full Git code :
I don't know the dataSource of the tableview behave,but from the error,it tells us clearly,that you have only one section,so you can't handle the section 2,because there isn't section 2.You can only handle the section 0.
You can delete the whole rows,and add row in section 0.besides,You should handle the self.students array,let it match the tableview rows,or it will crash.
UPDATE:
This commit resolve the issue Table scrolling
I have correct it,This version.
This commit resolve the issue Table scrolling get jerky
I have correct it,This version.

scrollToRowAtIndexPath throwing error

UITableView _contentOffsetForScrollingToRowAtIndexPath:atScrollPosition:]: section (3) beyond bounds (1).'
I've got an array of information being displayed at my tableview, and when I hit a button I'm wanting it to scroll down to a certain cell.
Initially I thought I was getting that error because of the way I was getting my index to jump to:
if let index = peopleListArray.index(where: { $0.UID == currentUser }) {
print(index)
let NSIndex = NSIndexPath(index: Int(index) )
tableView.reloadData()
print(peopleListArray.count)
print(NSIndex)
tableView.scrollToRow(at: NSIndex as IndexPath , at: .top, animated: true )
}
But then I replaced "let NSINDex... with
let NSIndex = NSIndexPath(index: 1 )
and it's still throwing the same error.
when I'm printing out my array count and my NSIndex I'm always getting an 8 for the count (which is correct) and I'm getting 3 for the NSINdexPath which is correct.
I could understand the error if the 3 was out of bounds of my array.count but it definitely isn't.
any ideas?
It seems the issue you are having is with the section and not with the row. Try to build the index like this:
NSIndexPath(item: index, section: 0)
Note the section is set to 0.
There are two options that may be wrong here:
you passed a section that is out of bounds, i.e with index less than 0 or bigger than the specified number of sections in
numberOfSectionsInTableView:(UITableView *)tableView
So just try to use 0 for the section number first.
the index of the row is out of bounds for the specified section (they can be different for each section)

Swift, adding a class array into a variable does not update array content in original class

I'm new to IOS so forgive me for my coding mistakes. I'm facing an issue where I have a tableView Controller with two sections. The first section has a button, when clicked, appends data into an array and deletes it's own row in the first section (i did this as there are extra non related rows in the first section). The number of rows in the second section is based upon array.count.
My issue is that I tried begin/end update, and it still doesn't work. Whenever I run the code below and run the startNewDay function (when the button is clicked), this error occurs:
'attempt to insert row 0 into section 1, but there are only 0 rows in section 1 after the update'
This doesn't make any sense, as I appended the array already before I inserted the new rows. The array was empty before I appended it. Shouldn't there be the same number of rows in the second section as array.count?
Table View Delegate code:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 2
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
if dataModel.lists[0].dayHasStarted == false {
return 2
} else {
return 1
}
} else {
if itemDoneCount == dataModel.lists[0].item.count && dataModel.lists[0].doneButtonVisible {
return dataModel.lists[0].item.count + 1
} else {
return dataModel.lists[0].item.count
}
}
}
startNewDay button function when pressed:
#IBAction func startNewDayDidPress(sender: AnyObject) {
dataModel.lists[0].dayHasStarted = true
dataModel.lists[0].startDate = NSDate()
addItemButton.enabled = !addItemButton.enabled
// deleting start new day button
let indexPath = NSIndexPath(forRow: 1, inSection: 0)
let indexPaths = [indexPath]
tableView.beginUpdates()
tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Fade)
tableView.endUpdates()
// Inserting new array elements and rows into 2nd section
let ritualsArray = dataModel.lists[0].rituals
var itemsArray = dataModel.lists[0].item
itemsArray.appendContentsOf(ritualsArray)
tableView.beginUpdates()
var insertRitualsArray = [NSIndexPath]()
for item in itemsArray {
let itemIndex = itemsArray.indexOf(item)
let indexPath = NSIndexPath(forRow: itemIndex!, inSection: 1)
insertRitualsArray.append(indexPath)
}
tableView.insertRowsAtIndexPaths(insertRitualsArray, withRowAnimation: .Top)
tableView.endUpdates()
}
SOLVED
The problem of this code is not at all related to the previous title of this thread, which may be misleading to people having the same issue as mine. Hence, I will be changing it. The previous title (for the curious) was :
"tableView.begin/end update not updating number of rows in section"
Just for others who might come across this issue, the issue isn't in the tableView delegate, nor is it in reloading the tableview data. For readability, I placed both dataModel.list[0].item into itemsArray and dataModel.list[0].item into ritualsArray. This apparently updates the itemsArray when appended but not the initial dataModel.list[0].item instead, which caused the second section in the tableView not to load the new number of rows, causing the error when inserting rows into non-existant rows.
Hence instead of:
let ritualsArray = dataModel.lists[0].rituals
var itemsArray = dataModel.lists[0].item
itemsArray.appendContentsOf(ritualsArray)
this solved it:
dataModel.list[0].item += dataModel.list[0].rituals
Hope it helps any beginner like me out there that comes across this issue.
Latest update
I found out recently that an array is of value type, and not reference type. Hence placing an array into a variable makes a copy of that array instead of serving as a placeholder for the original array.
Beginner mistake opps.
The error you are receiving means that the datasource contains a different number of items to however many there would be after inserting or deleting rows. This probably means that the data are not being inserted into your datasource array, or that the data do not match the criteria in the if statements in your numberOfRowsInSection function. To troubleshoot this, you should log the contents of the datasource array after modifying it to check what its contents are. If they are what you are expecting (I.e. The data have been added correctly) then the issue is in the way you are evaluating its contents to establish the number of rows. If the contents are not what you are expecting, then the issue is in the way you are inserting the data into the datasource array.
I had a similar problem after deleting a row. It seems that if
numberOfRowsInSection is not coherent (equal to last value -1) this error appears.
I see that there's a condition in your numberOfRowsInSection, this is perhaps the culprit

Adding row to Static UITableView: Invalid update: invalid number of rows in section 1

I'm trying to programatically insert a new row between two existing rows in my static UITableView, but it crashes saying:
'Invalid update: invalid number of rows in section 1. The number of
rows contained in an existing section after the update (6) 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 (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).'
This is the code I'm using to add the new row
if (indexPath.section == 1 && indexPath.row == 3) {
// If this is the first subtask we want it to appear under Sub Tasks
if (subTaskRow.count == 0) {
tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: 5, inSection: 1)], withRowAnimation: .Automatic)
// Added the first subtask
subTaskRow.addObject(5)
tableView.reloadData()
// Other sub tasks should appear below previous sub tasks
} else {
let row = (subTaskRow.lastObject as! Int) + 1
tableView.insertRowsAtIndexPaths([NSIndexPath(forRow: row, inSection: 1)], withRowAnimation: .Automatic)
// Keep track of subsequent subtasks
subTaskRow.addObject(row)
tableView.reloadData()
}
}

Reload Table view cell with animation (Swift)

Is there a way to reload specific UITableView cells with multiple sections with animations?
I've been using:
self.TheTableView.reloadSections(NSIndexSet(index: 1), withRowAnimation: UITableViewRowAnimation.Right)
This animates all the cells in the section though. The other option is to use:
self.TheTableView.reloadRowsAtIndexPaths(<#indexPaths: [AnyObject]#>,
withRowAnimation: <#UITableViewRowAnimation#>)
EDIT:
After using reloadRowsAtIndexPaths
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 (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Trying to find a smooth way of reloading a tableview by appending objects into an array.
Calling TheTableView.reloadData() before reloadRowsAtIndexPaths works, but the animation is glitchy. Is there another approach?
Why not just reload the cells directly? The indexPath contains the section and row so just keep track of which section you want to reload and fill the indexes in that array.
var indexPath1 = NSIndexPath(forRow: 1, inSection: 1)
var indexPath2 = NSIndexPath(forRow: 1, inSection: 2)
self.tableView.reloadRowsAtIndexPaths([indexPath1, indexPath2], withRowAnimation: UITableViewRowAnimation.Automatic)
Based on your comment you are looking to change your array and have the tableView animate in the changes. If that's the case you should consider using beginUpdates() and endUpdates() for UITableViews or even an NSFetchedResultsController so it handles all of the update animations cleanly.
self.tableView.beginUpdates()
// Insert or delete rows
self.tableView.endUpdates()
I'd recommend using NSFetchedResultsController if you're using Core Data as it simplifies this entire process. Otherwise you have to handle the changes yourself. In other words, if your array changes you need to manually remove or insert rows in the tableview using beginUpdates() and endUpdates().
If you aren't using Core Data, study this to grasp how it's all handled.
In Swift 3.0
We can reload particular row of particular section in tableview
let indexPath = IndexPath(item: 2, section: 0)
self.tableView.reloadRows(at: [indexPath], with: .automatic)
If you want to reload single section in swift 3.0 you can do this:
tableView.reloadSections(IndexSet(integer: 0), with: .automatic)
the define IndexSet in official document:
Manages a Set of integer values, which are commonly used as an index
type in Cocoa API. The range of valid integer values is
in (0, INT_MAX-1). Anything outside this range is an error.
so IndexSet is Set about index, just putting some Int values in [ ]
// for reload one section
tableView.reloadSections([section], with: .automatic)
// or using like this
tableView.reloadSections(IndexSet(integer: section), with: .automatic)
// for reload multi sections
tableView.reloadSections([1, 2, 3, section], with: .automatic)

Resources