Delete item in array, inside another array in Swift - iphone

I've got a An array which is a an array of Sections in the Table View.
I have got another array that contain arrays in it.
I displayed it on the table view correctly, but each time I want to delete it, I get an error.
I want to delete the exactly item in the array which is inside the array.
Here is what I got so far:
class ViewController: UIViewController, UITableViewDelegate {
#IBOutlet var tableView: UITableView!
var eachSection = ["a","ss","dd","heyyyyyy","3"]
var eachStep = [["z","zz"],["x","xx"],["c","cc","ccc"],["r","rr","rrr","rrrr"],["o"]]
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return eachSection.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let sectionString = Array(eachStep)[section]
return sectionString.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
// Configure the cell...
let sectionString = Array(eachStep)[indexPath.section]
cell.textLabel?.text = sectionString[indexPath.row]
print(sectionString)
return cell
}
func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
let sectionString = eachSection[section]
return sectionString
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath)
if (editingStyle == UITableViewCellEditingStyle.Delete) {
var sectionString = Array(eachStep)[indexPath.section]
sectionString.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
}
This is the error I got:
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 (2) must be equal to the number of
rows contained in that section before the update (2), 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)

This error contains answer for youre question.
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 (2) must be equal to the number of
rows contained in that section before the update (2), 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).
Look carefully what are you doing.
You delete items from one array that you are not using as data source.
var sectionString = Array(eachStep)[indexPath.section]
sectionString.removeAtIndex(indexPath.row)
Try this:
eachStep[indexPath.section].removeAtIndex(indexPath.row)

Related

Swift UITableView numberOfRows Error When Added or Removing Rows in Section

I've been experiencing a very annoying issue this week. I have a UITableView that generates a number of sections (with section headers), each section always having 3 rows.
I've implemented a hiding/showing section functionality when a section header is tapped, where all the rows in that section are either deleted or inserted accordingly. Everything works perfectly fine and as expected... 70% of the time. I find that once I start dragging around the UITableView a bit while tapping on headers and just being all around random, it crashes unexpectedly with the error:
*** 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 (3 inserted, 0 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out).'
Below is my relevant code:
#IBOutlet var pastWinnersTableView: UITableView!
var previousWeeks : [PreviousWeekData] = []
var hiddenSections = Set<Int>()
var firstTimeLoading = true ///This is needed to allow all the past week cells to start off as minimized
//This function runs in response to a section header being tapped in the table view.
#objc func expandCollapseSection(section: UIButton){
pastWinnersTableView.isUserInteractionEnabled = false
func indexPathsForSection() -> [IndexPath] {
var indexPaths = [IndexPath]()
for row in 0..<3 {
indexPaths.append(IndexPath(row: row, section: section.tag))
}
return indexPaths
}
//If the section was previously hidden...
if hiddenSections.contains(section.tag) {
hiddenSections.remove(section.tag)
pastWinnersTableView.insertRows(at: indexPathsForSection(), with: .fade)
//If the section was previously showing...
} else {
hiddenSections.insert(section.tag)
pastWinnersTableView.deleteRows(at: indexPathsForSection(), with: .fade)
}
}
//MARK: - REQUIRED METHODS
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if firstTimeLoading == true || self.hiddenSections.contains(section) { return 0 }
else { return 3 }
}
func numberOfSections(in tableView: UITableView) -> Int {
let numberOfPreviousWeeks = previousWeeks.count
if numberOfPreviousWeeks > 0 {return numberOfPreviousWeeks} else {return 0}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let weekNumber = previousWeeks[section].weekNumber
let headerButton = UIButton(type: .custom)
headerButton.layer.borderWidth = 1
headerButton.layer.borderColor = UIColor.black.cgColor
headerButton.layer.cornerRadius = 10
headerButton.layer.masksToBounds = true
headerButton.titleLabel?.font = UIFont(name: "Mouse Memoirs", size: 30)
headerButton.backgroundColor = task.hexStringToUIColor(hex: "5B3F22")
headerButton.setTitleColor(.white, for: .normal)
headerButton.titleLabel?.shadowColor = .black
headerButton.titleLabel?.shadowOffset = CGSize(width: 1, height: 1)
headerButton.addTarget(self, action: #selector(expandCollapseSection), for: .touchUpInside)
headerButton.tag = section
hiddenSections.insert(section)
headerButton.setTitle("Week \(String(weekNumber))", for: .normal)
return headerButton
}
//This footer is essentially just a small blank space.
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let footer = tableView.dequeueReusableCell(withIdentifier: "pastWinnersWeekFooterCell") as! PastWinnersWeekFooterCell
return footer
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { return 45 }
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat { return 5 }
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { return UITableView.automaticDimension }
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat { return 44 }
//PreviousWeekData[(weekNumber, firstPlacePlayers[(username, score)], secondPlacePlayers[(username, score)], thirdPlacePlayers[(username, score)]), (weekNumber,...)]
//This defines the tableViewCell that holds all 3 placement sections with all the winners for a given week
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let weekSection = indexPath.section
let weekData = previousWeeks[weekSection]
let cell = tableView.dequeueReusableCell(withIdentifier: "pastWinnersTableViewCell", for: indexPath) as! PastWinnersTableViewCell
cell.thisWeeksData = weekData
cell.winnersCollectionView.tag = indexPath.row
cell.frame = tableView.bounds;
cell.layoutIfNeeded()
cell.winnersCollectionView.reloadData()
cell.collectionViewHeight.constant = cell.winnersCollectionView.collectionViewLayout.collectionViewContentSize.height;
return cell
}
Any ideas why it would be crashing on me with that error only sometimes? (ie. once I start dragging it around a bit and THEN tapping on headers? Tapping on my section headers is always a 3 in, 3 out deal so I'm not sure why it's getting confused.
Thanks for the help!

Inserting Rows crashes in Swift

I am facing crash while inserting cells in TableView. I have tried below links but not sure what is the issue
Link1 Link2 Link3 and many
Below is my code
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return names.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell: UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = names[indexPath.row]
return cell
}
Data Source code is below
private var names: [String] = (50...99).map { String($0) }
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
self.appendCells()
}
}
private func appendCells() {
names = (0...49).map { String($0) } + names
customTableView.beginUpdates()
let indexPat = IndexPath(row: names.count - 1, section: 0)
customTableView.insertRows(at: [indexPat], with: .fade)
customTableView.endUpdates()
}
I cant understand what am I missing here. Sorry if I am doing foolish. Please let me know If I have to explain more.
I am getting error :
reason: 'Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (100) must be equal to the number of rows contained in that section before the update (50),
The problem is the mismatch in the number of values you add to the data model and the number of rows you add to the table view.
The line:
names = (0...49).map { String($0) } + names
adds 50 new values to your data model.
But you then only tell the table view that there is one new row. This is what you are being told in the error message.
You need a loop to build up an array of 50 index paths.
private func appendCells() {
names = (0...49).map { String($0) } + names
var paths = [IndexPath]()
for row in 0...49 {
let indexPath = IndexPath(row: row, section: 0)
paths.append(indexPath)
}
customTableView.insertRows(at: paths, with: .fade)
}
The error clearly states that you are adding indexPath with row = 99, section = 0, while in reality, it's biggest indexpath holds values: row = 49, section = 0.
Before insertion, tableview calls datasource method numberOfRowsInSection - this method must return your newer, bigger array count (100), not the earlier one (50).
1) Your model before ‘appendCells’ has 50 names count.
2) After call your mode is equal 100 names count but you only instert one cell at index path = 99? You need to insert 50 more rows instead of one.
I was having this problem, and it worked with the following approach
func insertRow(entries: [String]) {
tableView.performBatchUpdates({
self.tableView.insertRows(at: [IndexPath(row: entries.count - 1, section: 0)], with: .bottom)
}, completion: nil)
}

Deleting the last row and loading multiple rows at the same time

When I delete the last row from a tableview with:
tableView.deleteRows(at: [indexPath], with: .middle)
and change my table's data source to an another array:
if orders.isEmpty {
return ABCDArray.count
} else {
return orders.count
}
It gives me this error:
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 (20) 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 (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).'
How can I solve that? What is the problem with changing data source?
You need to remove the object from your data array before you call tableView.deleteRows(at: [indexPath], with: .middle). So, your code should look like this:
// Editing of rows is enabled
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
//when delete is tapped
orders.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: .middle)
}
}
Was an interesting question so I decided to make it a try.
I made a quick implementation with that, I am sure it's not the prettiest way to do it but if you can be inspired by that maybe:
class ViewController : UITableViewController {
var orders = [1,2,3,4]
var ABCDArray = ["A","B","C","D"]
var currentCellNumber = 0
override func viewDidLoad() {
super.viewDidLoad()
currentCellNumber = orders.count
tableView?.delegate = self
tableView?.dataSource = self
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
if orders.isEmpty {
cell.textLabel?.text = ABCDArray[indexPath.row]
} else {
cell.textLabel?.text = String(orders[indexPath.row])
}
return cell
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return currentCellNumber
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if !orders.isEmpty {
orders.remove(at: indexPath.row)
currentCellNumber -= 1
tableView.deleteRows(at: [indexPath], with: .middle)
if orders.isEmpty {
changeDataSource(newDataSourceArray: ABCDArray)
}
}
}
func changeDataSource(newDataSourceArray array: Array<Any>) {
var newCellIndexPaths = [IndexPath]()
for row in 0...array.count-1 {
print(row)
currentCellNumber = row+1
print(currentCellNumber)
let insertionIndexPath = IndexPath(row: row, section: 0)
print(insertionIndexPath)
newCellIndexPaths.append(insertionIndexPath)
}
tableView.insertRows(at: newCellIndexPaths, with: .automatic)
}
}
If you have some question donut hesitate> Hope it helps

how can I trim my UITableViewController so that it shows always only 10 last rows?

Currently in my Swift app I have a UITableViewController. I already implemented paging when user scrolls to the top of the table - it loads more data and fills the table there.
But now I want to apply also the other feature - when user scroll to the very bottom of the table, it should truncate rows that added before thanks to paging and leave only last 10 cells.
So far my code looks like this:
override func tableView(_ tview: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.row == 0 { // first cell
print("this is first cell - it works")
loadMoreItems()
}
if indexPath.row == messages.count - 1 { // last cell
if(self.messages.count > 10){
leaveTenVisibleMessages()
}
}
let cell = tview.dequeueReusableCell(withIdentifier: "chat") as! SingleCommentCell
let msg = self.messages[indexPath.row]
.
.
.
and the method leaveTenVisibleMessages looks as follows (so far):
func leaveTenVisibleMessages(){
print("last cell before \(self.messages.count)")
if(self.messages.count > 10){
self.messages = Array(self.messages.suffix(10))
}
print("last cell after \(self.messages.count)")
//tview.reloadData()
}
however, even though I see that trimming the array worked:
last cell before 11
last cell after 10
I'm constantly getting error:
fatal error: Index out of range
I think the reason is that cellforrow at the moment of runtime expects more than 10 entries in array messages. How can I refresh only 10 rows then?
I just checked and the problem is this line:
let msg = self.messages[indexPath.row]
e.g. my code refers to the indexPath.row = 29 and messages array has only 10 records
Check your code that returns the numberOfRowsInSection.
e.g.
func tableView(
_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int
{
let result = self.messages.count
NSLog("numberOfRowsInSection = %d", result)
return result
}

TableView on simulator is not referencing correctly

Hello and how is everyone?
When I run the simulation of my application I go to select the first object in the table. Nothing happens. I then go and select the second object and it performs the action that the first object was supposed to. Basically the first object is not active until I select it and then select another object.
Maybe this will help:
I turn the simulator on. I select the first object nothing happens(I have a print("what row I selected") within the didSelect function to make sure it is working.). But again no printout until I hit the second object, the second object should print #1 row selected but it says #0 row selected and then performs the segue setup for 0#. After I come back from the segue the first object still performs the same.
Here is my tableView Code:
var objects: NSMutableArray! = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
self.objects.addObject("help")
self.objects.addObject("me")
self.objects.addObject("please")
self.objects.addObject("thankyou")
self.tableView.reloadData()
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
print(self.objects.count)
return self.objects.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! TableViewCell
cell.titleLabel.text = self.objects.objectAtIndex(indexPath.row) as? String
return cell
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
print("You selected cell #\(indexPath.row)!")
Thanks for the help.