inserting a fake row#indexPath, iOS - ios

Im creating a new table, and within it, Im inserting a new row with fake text, but I get the crash report
Terminating app due to uncaught exception
'NSInternalInconsistencyException', reason: 'attempt to insert row 5
into section 0, but there are only 5 rows in section 0 after the
update
after some fiddling around, I changed my code from:
#IBAction func add() {
let newRow = ChecklistItems(text: "Im the new Row", checked: false)
items.append(newRow)
let index = items.count
let indexPath = NSIndexPath(forRow: index, inSection: 0)
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Left)
}
TO THIS:
#IBAction func add() {
let index = items.count
let newRow = ChecklistItems(text: "Im the new Row", checked: false)
items.append(newRow)
let indexPath = NSIndexPath(forRow: index, inSection: 0)
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Left)
}
can someone explain to me the actual details why this switch made the code work?
thank you so much~

Welcome to SO.
In your first version, you add an item to your items array, then create an indexPath which points to the count of the array (So if the array contains 5 items, you create an indexPath value of row:5, section:0)
However, arrays are zero-based. If an array contains 5 elements, valid array indexes are 0 - 4. The same goes for indexPath values. The highest valid row value is totalRows - 1. If the array contains 5 elements, 5 is not a valid array index.
In the second version of your code, index = the item count BEFORE adding an item. So if the row contains 4 items, count = 4, you add an item, and now items contains 5 items and 4 is a valid index into the array, so the insert does not refer to an item that's beyond the end of your items array.

Because the indexPath is beginning with 0, so it's max value is the items.count - 1

Because count is starting from 1, but NSIndexPath and also array index starting from 0.
so lets say you have an array of ("a", "b", "c"), array count is 3. but the index of it is 0, 1, 2.
so with your first code, you add new item and choose the index based on its count which is beyond its array index. for example you add new item "d" to example array above, the count will be changed to 4. But the max index is 3, which is why it throws error.
with your second code, you get the index based on count first before adding. So you set index for item d with 3, because the array only contain a, b, c and then add d to array.
it is working for you right now, but it will potentially create a mismatch between your array index and your tableView index.
A good way to retrieve object index is using array function indexOf.

Related

How to change default indexPath 0 to 1 of tableView in swift3?

I want to display my data in tableView but I want to start indexPath from 1. But I am not getting any solution .Is there any way I can start indexPath from 1
output I am getting is:
myData 1,2,3,4,5 and indexPath is 0,1,2,3,4
what I want :
myData 1,2,3,4,5 and indexPath is 1,2,3,4,5
because of indexPath start from 0 my data at index 1 get assigned to 0 and so on.
CAN WE INCREMENT indexPath.row by one before loop starts
You can't change indexPath but you can display data as below func
let lblTitle: UILabel? = (cell?.viewWithTag(102) as? UILabel)
lblTitle?.text = "\(indexPath.row + 1)" // here you need to add 1
IndexPath uses arrays which start at index 0.
Each index in an index path represents the index into an array of children
Thank you for your response but I get the solution for this
Here is the solution:
mainArrayRoll.insert("0", at: 0)
I have added an default value to my array so every time indexPath start from zero it deals with my default value and I get my proper output from index location 1.

How to compare 2 arrays and append last items to second array iOS Swift 3

Let's say i have a firstArray of PFObject[](from Parse SDK), and it has 7 items, i make a secondArray out of firstArray:
secondArray = firstArray
then i call a query to the database to retrieve updated data, they are now 10 items in firstArray.
I do something like this:
if firstArray.count > secondArray.count {
let lastItems = firstArray.count - secondArray.count
// lastItems = 3
}
How do i append those 3 last items to the end of the secondArray in their specific order?
secondArray append item 8
secondArray append item 9
secondArray append item 10
I don't want to reloadData(), i just want to add the last 3 rows to my TableView like WhatsApp does, for example, otherwise the TableView will scroll to the top.
Thanks!
First append data to second array
secondArray.append(firstArray[index])
Then
self.yourTableView.beginUpdates()
var insertedIndexPaths = [indexpath_where_you_want_insertData]
self.yourTableView.insertRowsAtIndexPaths(insertedIndexPaths, withRowAnimation: .Fade)
self.yourTableView.endUpdates()
Since you ask for a way to append it, this should work.
if firstArray.count > secondArray.count {
for i in secondArray.count..<firstArray.count {
secondArray.append(firstArray[i])
}
}
Hope this helps!
if firstArray and secondArray are same
you can just do
secondArray = firstArray
and if in case you have updated secondArray and wants to append new data only
var counter = 0
while secondArray.count != firstArrayCount {
secondArray.append(firstArray[firstArray.count+counter])
counter += 1
//insert new row in table
}
If you wish to update your table view without reloading it, you'll have to use
func insertRows(at indexPaths: [IndexPath], with animation: UITableViewRowAnimation)
You should look at this doc for better understanding of how to implement your task:
https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/TableView_iPhone/ManageInsertDeleteRow/ManageInsertDeleteRow.html

Remove / delete selected cells from UICollectionView causes index out of bounds [sometimes]

I have a comments array declared as: var comments: [String] which I populate it with some Strings and I also have a UICollectionView within which I present the comments. My code is the following when I try to delete the selected cells from the UICollectionView:
if let indexPathsForSelectedItems = collectionView.indexPathsForSelectedItems {
for indexPath in indexPathsForSelectedItems {
comments.remove(at: indexPath.item) //I have only one section
}
collectionView.deleteItems(at: indexPathsForSelectedItems)
}
The issue is that sometimes when I delete the selected items, it creates an out of bounds exception on the comments array.
However when I use the following approach (create a copy array and replace the original one with its copy) no problem occurs:
var indexes: [Int] = []
for indexPath in indexPathsForSelectedItems {
indexes.append(indexPath.item)
}
var newComments: [String] = []
for (index, comment) in comments.enumerated() {
if !indexes.contains(index) {
newComments.append(comment)
}
}
comments = newComments
Why is this happening?
I am using Swift 3 and XCode 8.2.1
Sorting
If you're not sure that indexPathsForSelectedItems are sorted in descending order, and hence always deletes the highest index first, you will eventually run into an out of bounds. Deleting an item will change the indices for all array elements with higher indices.
You probably want to use indexPathsForSelectedItems.sorted(by: >).

TableView Extension indexPathsFor `Un` SelectedRows

Everybody knows about the indexPathsForSelectedRows - but here come the time that i need to know the rows that are not selected
I would like to make an extension of it.
Is there any chance somebody already have done it or have an idea how this can be done?
If there is only one section in the table view you could simply
Map all row indexes from indexPathsForSelectedRowsto an Int array.
let rowIndexes = indexPathsForSelectedRows.map { $0.row }
Create an Int array from the indexes in the dataSourceArray
let allIndexes = 0..<dataSourceArray.count
Filter the indexes which are not in allIndexes and create new index paths
let indexPathsForDeselectedRows = allIndexes.filter {!rowIndexes.contains($0) }.map {NSIndexPath(forRow: $0, inSection: 0)}
If there are multiple sections it's a bit more complex.

How Can I Remove Only Selected Index Paths From An Array In Swift?

I have a mutable array:
var responseArray = ["yes", "no", "no way", "of course", "for sure", "not a chance", "positively"]
The responseArray is the data source for my table view which allows for multiple selections during editing.
I am capturing the selected index paths:
let paths = tableView.indexPathsForSelectedRows()
I can return and verify each selected indexPath of my tableView by running println(paths).
I have read the documentation for the indexPathsForSelectedRows method and understand that it returns an array of index paths which I have sorted by row.
What I cannot understand is how I can use the returned array of index paths to remove the data from the responseArray for each row that is selected for deletion in the table view.
After reading through some documents, is it correct of me to believe that I cannot remove any data from the `responseArray' as I am enumerating over it? For example:
#IBAction func deleteButtonPressed(sender: AnyObject) {
if responseArray.count > 0 {
if let paths = self.tableView.indexPathsForSelectedRows() {
var sortedArray = paths.sorted({$0.row < $1.row})
// Remove index paths from responseArray
for i in sortedArray {
responseArray.removeAtIndex(i.row)
}
self.tableView.reloadData()
}
}
}
I am able to remove each row from the table view one by one, but when I select the first and last rows, all of the rows, or any other combination of rows for deletion, I get fatal error: Array index out of range. However, if I select two adjacent rows for deletion, the desired result is achieved and those two rows are removed from the table view.
I know that there is something that I am missing, but as a new programmer I have been unable to resolve this issue for three days now. What is it that I am not doing correctly?
Here's your array: [ A, B, C, D ]
Let's say you want to delete A and D at index 0 and 3 respectively, one at a time:
deleteAtIndex(0) gives: [ B, C, D ]
deleteAtIndex(3) gives: Out of bounds exception
Edit:
Ok, to avoid complicating things, why not just always delete the highest index first by reversing your sort: {$1.row < $0.row}
For future reference, in addition to the answers given already you could simplify it further by reversing the selected indices:
#IBAction func deleteButtonPressed(sender: AnyObject) {
self.tableView.selectedRowIndexes.reverse().forEach { x in
responseArray.removeAtIndex(x)
}
self.tableView.reloadData()
}
Since your paths array is sorted, you know that every time you delete an element from the array, higher indices will now be one less than they were. You can simply keep an incrementing offset to apply to your deletions -
#IBAction func deleteButtonPressed(sender: AnyObject) {
if responseArray.count > 0 {
if let paths = self.tableView.indexPathsForSelectedRows() {
var sortedArray = paths.sorted({$0.row < $1.row})
var offset=0;
// Remove index paths from responseArray
for i in sortedArray {
responseArray.removeAtIndex(i.row-offset)
offset++
}
self.tableView.reloadData()
}
}
}

Resources