Fatal Error: Index out of range tableview - ios

Can anybody see where i am going wrong? The tableView controller should display either 1 or 4 sections (depending on input from the previous View Controller) along with a dynamic number of rows coming from the array(s).
The program is supposed to populate each section with a different array however i am receiving an Index out of range error in the third case in the Switch statement. The sections and rows are set in the viewdidLoad() method and the selectPic() method changes houseImg variable accordingly.
I'm using Swift 4.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
if checkData == 0 {
//Inserting one section and filling the cells with the array
saveCount = pupilNames.count
if saveCount > counter{
cell.imageView?.image = houseImg
cell.textLabel?.text = "Pupil Name: \(pupilNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(pupilScores[indexPath.row])"
counter = counter+1
}
checkData = 2
return cell
}
else if checkData == 1 {
//Inserting four sections and populate 4 arrays for each section
var countIf: Int = 0
var secondCount: Int = 0
houseCount = 4
if checkCount > houseCont {
switch houseCont {
case 0:
chosenHouse = 1
selectPic()
countIf = pupilNames.count
repeat {
cell.imageView?.image = houseImg
cell.textLabel?.text = "Pupil Name: \(pupilNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(pupilScores[indexPath.row])"
secondCount = secondCount+1
} while countIf > secondCount
case 1:
chosenHouse = 2
selectPic()
countIf = secondNames.count
secondCount = 0
repeat {
cell.imageView?.image = houseImg
cell.textLabel?.text = "Pupil Name: \(secondNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(secondScores[indexPath.row])"
secondCount = secondCount+1
} while countIf > secondCount
case 2:
chosenHouse = 3
selectPic()
countIf = thirdNames.count
secondCount = 0
repeat {
cell.imageView?.image = houseImg
//*******Error occurring on Pupil Name line *********
cell.textLabel?.text = "Pupil Name: \(thirdNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(thirdScores[indexPath.row])"
secondCount = secondCount+1
} while countIf > secondCount
case 3:
chosenHouse = 4
selectPic()
repeat {
cell.imageView?.image = houseImg
cell.textLabel?.text = "Pupil Name: \(fourNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(fourScores[indexPath.row])"
secondCount = secondCount+1
}while countIf > secondCount
default:
houseCont = 5
checkData = 2
}
houseCont = houseCont+1
}
}
return cell
}
Edit:
'houseCount' is sent from the previous view controller and contains a value of one or four. 'saveCount' contains a number created by adding up the count of all arrays, this is done in the method that populates the arrays.
saveCount = pupilNames.count+secondNames.count+thirdNames.count+fourNames.count
override func numberOfSections(in tableView: UITableView) -> Int{
return houseCount
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
return saveCount
}
Final Edit: Thanks for all the help everyone, it was a mixture of both answers that helped me to resolve my error. This is my final code that fixed my problem, i removed the if statement and the repeat loop as well as changed some bits of code in order to fix my problem.
else if checkData == 1 {
houseCount = 4
switch indexPath.section {
case 0:
chosenHouse = 1
selectPic()
cell.imageView?.image = houseImg
cell.textLabel?.text = "Pupil Name: \(pupilNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(pupilScores[indexPath.row])"
case 1:
chosenHouse = 2
selectPic()
cell.imageView?.image = houseImg
cell.textLabel?.text = "Pupil Name: \(secondNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(secondScores[indexPath.row])"
case 2:
chosenHouse = 3
selectPic()
cell.imageView?.image = houseImg
cell.textLabel?.text = "Pupil Name: \(thirdNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(thirdScores[indexPath.row])"
case 3:
chosenHouse = 4
selectPic()
//saveCount = fourNames.count
cell.imageView?.image = houseImg
cell.textLabel?.text = "Pupil Name: \(fourNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(fourScores[indexPath.row])"
default:
houseCont = 5
checkData = 2
}
houseCont = houseCont+1
}

Looking at the following code, a couple of alarm bells are going off:
chosenHouse = 3
selectPic()
countIf = thirdNames.count
secondCount = 0
repeat {
cell.imageView?.image = houseImg
//*******Error occurring on Pupil Name line *********
cell.textLabel?.text = "Pupil Name: \(thirdNames[indexPath.row])"
cell.detailTextLabel?.text = "Score: \(thirdScores[indexPath.row])"
secondCount = secondCount+1
} while countIf > secondCount
Firstly the use of repeat. The code inside always gets executed at least once. I think this would be an error in the case that thirdNames.count == 0
However, even more oddly, the content of the repeat loop does not change between iterations. You don't do anything that depends on the only thing that changes - secondCount. If countIf were 10, you'd simply assign the same name and score to the same two controls 10 times.

I am not sure but according to me in the last switch statement,when it is executing there is problem while inserting to the sections because it might be exceeding index path row count somehow. Also the way code is written not taking into account the indexPath.section .

Related

Why is the cell not resizing with its content?

I have read all of the similar questions I could find here and applied all of the proposed solutions ... none of them works.
The table view is a grouped-one, the number of lines for the textLabel and the detailTextLabel is set to 0 both in code and in the storyboard, the string data is made of a multiline string created via "\n" characters...
Setting tableView.estimatedRowHeight to something else rather than the automatic 43.5 and then tableView.rowHeight = UITableView.automaticDimension as suggested in some other questions does absolutely nothing... or if I put something like 100, it will resize all the cells instead of just the ones which have multiple lines of text.
I am unfamiliar with this type of behaviour from table views: in my experience, simply putting the number of lines to 0 for the labels just worked...
Why do I get this result?
EDIT: I have also tried to implement the heightForRowAt method, passing in the desired sections and rows but nothing at all changed ... I am lost.
EDIT (2): Removed previous snippet and inserted ViewController source code: the only thing I can show that is relevant to the cell is this, as the only property is an instance of a custom data type that is populating the table view (and it is being very good at that) and viewDidLoad() has nothing of mine added inside of it.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
cell.textLabel?.numberOfLines = 0
cell.detailTextLabel?.numberOfLines = 0
func configureTextLabels(withString labelText: String, and detailText: String) {
cell.textLabel?.text = labelText
cell.detailTextLabel?.text = detailText
}
if let country = detailCountry {
switch indexPath.section {
// insert 14 different cases here
case 14:
func populateRows() {
let regionalBlocs = country.regionalBlocs
for i in 0 ..< regionalBlocs.count {
switch indexPath.row {
case 0 + (i * 4): configureTextLabels(withString: "Name: ", and: "\(regionalBlocs[i].name)")
case 1 + (i * 4): configureTextLabels(withString: "\tAcronym: ", and: "\(regionalBlocs[i].acronym)")
case 2 + (i * 4):
var detailText = ""
let otherNames = regionalBlocs[i].otherNames
if !otherNames.isEmpty {
for name in otherNames {
if name == otherNames.last {
detailText += name
} else {
detailText += "\(name)\n"
}
}
configureTextLabels(withString: "\tOther names: ", and: detailText)
} else {
configureTextLabels(withString: "", and: "No other names found")
}
case 3 + (i * 4):
var detailText = ""
let otherAcronyms = regionalBlocs[0].otherAcronyms
if !otherAcronyms.isEmpty {
for acronym in otherAcronyms {
if acronym == otherAcronyms.last {
detailText += acronym
} else {
detailText += "\(acronym)\n"
}
}
configureTextLabels(withString: "\tOther acronyms: ", and: detailText)
} else {
configureTextLabels(withString: "", and: "No other acronym found")
}
default: break
}
}
}
let regionalBlocs = country.regionalBlocs
if regionalBlocs.isEmpty {
cell.textLabel?.text = ""
cell.detailTextLabel?.text = "No regional bloc data found for this country"
} else {
populateRows()
}
}
return cell
}
You can look answer in here: Add multiple lines to detailTextLabel in UITableViewCell
But I suggest you create custom cell for this case, your issue is UIKit's bug.

Remove cells if titleLabel.string / cell content is empty

I have the following problem.
example
If a cell has no content, I want to hide the cell. The logic as you can see allows, that constantly 5 cells get returned:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(section == 0){
return 1
}
return 5
}
Here is the logic of the actual table view:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section{
case 0:
let cell = tableView.dequeueReusableCell(withIdentifier: "CampusGoogleMapsTableViewCell", for: indexPath) as! CampusGoogleMapsTableViewCell
if let building = self.selectedPOIOffice.room?.building{
cell.setMarkerForSelectedBuilding(building)
}
return cell
case 1:
let cell = tableView.dequeueReusableCell(withIdentifier: "CampusTableViewCell", for: indexPath) as! CampusTableViewCell
let iconLabel: UILabel = cell.iconLabel
let titleLabel: UILabel = cell.titleLabel
iconLabel.font = UIFont.fontAwesome(ofSize: 25, style: .solid)
switch indexPath.row{
case 0:
//name
titleLabel.text = selectedPOIOffice.name
titleLabel.textColor = UIColor.black
titleLabel.alpha = 0.8
iconLabel.text = FontAwesomeIcons.University.getIcon()
case 1:
//Phone
titleLabel.text = selectedPOIOffice.phone
titleLabel.textColor = HsKAmpusColors.Red
iconLabel.text = FontAwesomeIcons.Phone.getIcon()
case 2:
//email
titleLabel.text = selectedPOIOffice.email
titleLabel.textColor = HsKAmpusColors.Red
iconLabel.text = FontAwesomeIcons.Mail.getIcon()
case 3:
//Opening Hours
if(titleLabel.text == nil){ break}
titleLabel.text = selectedPOIOffice.openingHours
titleLabel.textColor = UIColor.black
titleLabel.alpha = 0.8
iconLabel.text = FontAwesomeIcons.Clock.getIcon()
case 4:
//Location
titleLabel.text = selectedPOIOffice.room?.roomAndBuildingString ?? ""
titleLabel.textColor = UIColor.black
titleLabel.alpha = 0.8
iconLabel.text = FontAwesomeIcons.PositionMarker.getIcon()
default:
break
}
return cell
default:
return UITableViewCell()
}
}
I think I could solve it with a simple for-statement to check if every cell has any content. Can you help me please with the application?
You are going about this all wrong. cellForRowAt is not the place to attempt to hide a cell. By the time it is called, the cell is going to be shown.
Do one of two things:
Update your data model used by your data source methods to only include the data you want to display. Or...
Implement heightForRowAt to return 0 for rows you don't wish to see.
"If a cell has no content, I want to hide the cell."
That sentence shows a basic misunderstanding of how table views and collection views work. Table views display tabular data from a data model. If you have empty entries in your model, remove them from the model before giving it to the table view.

how to select only one cell from the tableviewsection

I have used UITableViewSection. So i have listed the data which i needed.
But problem in selecting the UITableViewCell.
This is my code while selecting the cell.
this is the screen shot
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Section \(indexPath.section), Row : \(indexPath.row)")
let model = questionViewModel.titleForHeaderInSection(atsection:indexPath.section)
print(model.question)
// reviewViewModel.question = model.id
print(model.id)
let value: Int = model.id
let string = String(describing: value)
//let x: Int? = Int(model.id)
questionViewModel.question_id = string
questionViewModel.question = model.question
let optiondatasourcemodel:NH_OptionDataSourceModel = NH_OptionDataSourceModel(array: questionViewModel.tableArray[indexPath.section] as? [String] )
print(optiondatasourcemodel.dataListArray?.count)
let optionviewmodel:NH_OptionViewModel = NH_OptionViewModel(withdatasource: optiondatasourcemodel)
let optionmodel = optionviewmodel.datafordisplay(atindex:indexPath)
let modele = questionViewModel.titleForHeaderInSection(atsection: indexPath.section)
print(modele.buttontype)
optionviewmodel.button = modele.buttontype
let cell = tableview.cellForRow(at: indexPath) as? NH_QuestionListCell
print(questionViewModel.questionlist)
print(questionViewModel.questions)
print(questionViewModel.answers)
questionViewModel.question(answer:questionViewModel.question_id!)
questionViewModel.answer(answer: optionmodel.values!)
questionViewModel.questionlist(answer: questionViewModel.question!)
if optionviewmodel.button == "1" {
cell?.imagebutton.setImage(UIImage(named: "checkbox_check.png"), for: .normal)
self.selected = 1
questionViewModel.imagename = "1"
}
else if optionviewmodel.button == "2" {
self.selected = 1
cell?.imagebutton.setImage(UIImage(named: "radio_check.png"), for: .normal)
questionViewModel.imagename = "2"
}
}
according to the screenshot there are two question and have two options.From that i need to select only one option .
EX:are u fine:-question
answer:-yes
just select only one cell.
Once i select yes and i select no option then the data should remove from the array.
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
let optiondatasourcemodel:NH_OptionDataSourceModel = NH_OptionDataSourceModel(array: questionViewModel.tableArray[indexPath.section] as? [String] )
print(optiondatasourcemodel.dataListArray?.count)
let optionviewmodel:NH_OptionViewModel = NH_OptionViewModel(withdatasource: optiondatasourcemodel)
let optionmodel = optionviewmodel.datafordisplay(atindex:indexPath)
let modele = questionViewModel.titleForHeaderInSection(atsection: indexPath.section)
print(modele.buttontype)
optionviewmodel.button = modele.buttontype
let cell = tableview.cellForRow(at: indexPath) as? NH_QuestionListCell
print(optionmodel.values)
questionViewModel.removequestion(question:questionViewModel.question!)
questionViewModel.removeanswer(answer:optionmodel.values!)
questionViewModel.removequestionid(removequestionid:questionViewModel.question_id!)
let model = questionViewModel.titleForHeaderInSection(atsection:indexPath.section)
print(model.question)
// reviewViewModel.question = model.id
print(model.id)
let value: Int = model.id
let string = String(describing: value)
//let x: Int? = Int(model.id)
questionViewModel.question_id = string
questionViewModel.question = model.question
questionViewModel.answer = optionmodel.values
if optionviewmodel.button == "1" {
cell?.imagebutton.setImage(UIImage(named: "checkbox_uncheck.png"), for: .normal)
self.selected = 1
questionViewModel.imagename = "1"
}
else if optionviewmodel.button == "2" {
self.selected = 1
cell?.imagebutton.setImage(UIImage(named: "radio_uncheck.png"), for: .normal)
questionViewModel.imagename = "2"
}
}
this is the code .
But according to this some problem.
Problems:-
when i select the second section that means.....
question:-are u happy
for that i selected option as yes.At that time ,i already for first question i have choose one option as yes .And checkbox image will display as click.
But while i select for next question the checkbox image of first question cell is disappear.How to fix that issues.
while selecting the second question option data is not appending in the array.
it is appending when i deselect the same cell
So how to fix the problem?

HeightForRowAt indexPath issue

I' m working to an e-commerce app and I have an issue when trying to set the height of a specific row. When I choose the category the table reloads and depending on the category i choosed more or less rows are coming through the api.
So with this setup it s working when i select a specific category but when i choose another it will mess my rows and will increase the height to another
let firstCellHeight:CGFloat = 265
let addBtnCellHeight:CGFloat = 60
let phoneCellHeight: CGFloat = 95
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
let cellIdentifier = self.tableCells[indexPath.row]
let height:CGFloat!
switch cellIdentifier {
case "AddAdImagesCell":
height = self.firstCellHeight
case "AddBtnCell":
height = self.addBtnCellHeight
case "ExtraFieldCell":
if indexPath.row == 4 {
height = self.phoneCellHeight
} else {
height = 44
}
default:
height = 44
}
return height
}
For the cell for row i m having this code:
case "ExtraFieldCell":
let currentField = self.extraFields[indexPath.row - self.firstExtraFieldIndex]
switch currentField.type {
case "select":
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldSelectCell")!
if currentField.name == "area"
return cell
case "text":
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldTextCell") as! FiltersTFCell
cell.label.text = "\(currentField.label):"
return cell
case "checkbox":
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldCheckboxCell") as! CheckboxCell
cell.fieldLabel.text = currentField.label
return cell
default:
let cell = UITableViewCell()
return cell
}
So how to set the height 95 always for the row with the case "text"
try using self.tableView.rowHeight when setting the cell, I tried to apply with your example code below, if you have any questions call me back.
case "ExtraFieldCell":
let currentField = self.extraFields[indexPath.row - self.firstExtraFieldIndex]
switch currentField.type {
case "select":
self.tableView.rowHeight = firstCellHeight
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldSelectCell")!
if currentField.name == "area"
return cell
case "text":
self.tableView.rowHeight = addBtnCellHeight
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldTextCell") as! FiltersTFCell
cell.label.text = "\(currentField.label):"
return cell
case "checkbox":
self.tableView.rowHeight = phoneCellHeight
let cell = tableView.dequeueReusableCell(withIdentifier: "ExtraFieldCheckboxCell") as! CheckboxCell
cell.fieldLabel.text = currentField.label
return cell
default:
self.tableView.rowHeight = 44
let cell = UITableViewCell()
return cell
}
importantly, when is test comment or remove this method
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { ... }

Swift, XCode6: Adding and deleting new sections in uitableview

I am trying to create a view which has a button at the top and a uitableview right beneath it. The uitableview has a section of 3 rows by default. What I wish to achieve is that whenever the button is pressed, I wish to add a new section of 4 rows. The first row of every newly added section should delete the section on selection. Sections are appended to the bottom of the existing sections as the top button is pressed. However, they can be deleted from the middle.
I have posted my code below. Sections are being added fine. However, deletion is not happening properly. If I select the 0th row of a newly added section, nothing happens. But if I select the 0th row and then select the 1st row of the same section, the section gets deleted. I am also not sure about my add new section logic. I am new to iOS development, please help me in getting this right, thanks in advance!
#IBOutlet weak var tableView: UITableView!
var retailProduct: RetailProduct?
let mandatoryVariants = 1
var additionalVariants = 0
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = UITableViewCell()
if indexPath.section == 0 {
switch indexPath.row {
case 0:
var variantImagesCell = tableView.dequeueReusableCellWithIdentifier("variantImagesCell") as! VariantImagesCell
cell = variantImagesCell
break
case 1:
var variantColorCell = tableView.dequeueReusableCellWithIdentifier("addColorCell") as! AddColorCell
variantColorCell.colorLabel.text = "Color"
if let color = retailProduct?.colorVariants[0].color {
variantColorCell.colorName.text = color.description
} else {
variantColorCell.colorName.text = "Select Color"
}
cell = variantColorCell
break
case 2:
var variantQuantityCell = tableView.dequeueReusableCellWithIdentifier("addQuantityCell") as! AddQuantityCell
variantQuantityCell.addQuantityLabel.text = "Size and Quantity"
variantQuantityCell.quantity.text = String(0)
cell = variantQuantityCell
break
default:
break
}
} else {
switch indexPath.row {
case 0:
var deleteVariantCell = tableView.dequeueReusableCellWithIdentifier("deleteCell") as! DeleteCell
cell = deleteVariantCell
break
case 1:
var variantImagesCell = tableView.dequeueReusableCellWithIdentifier("variantImagesCell") as! VariantImagesCell
cell = variantImagesCell
break
case 2:
var variantColorCell = tableView.dequeueReusableCellWithIdentifier("addColorCell") as! AddColorCell
variantColorCell.colorLabel.text = "Color"
if let color = retailProduct?.colorVariants[0].color {
variantColorCell.colorName.text = color.description
} else {
variantColorCell.colorName.text = "Select Color"
}
cell = variantColorCell
break
case 3:
var variantQuantityCell = tableView.dequeueReusableCellWithIdentifier("addQuantityCell") as! AddQuantityCell
variantQuantityCell.addQuantityLabel.text = "Size and Quantity"
variantQuantityCell.quantity.text = String(0)
cell = variantQuantityCell
break
default:
break
}
}
//println("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ " + String(indexPath.section) + " " + String(indexPath.row))
return cell
}
func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ " + String(indexPath.section) + " " + String(indexPath.row))
if (indexPath.section > 0) && (indexPath.row == 0) {
additionalVariants--
let indexSet = NSMutableIndexSet()
indexSet.addIndex(indexPath.section)
self.tableView.deleteSections(indexSet, withRowAnimation: UITableViewRowAnimation.Automatic)
}
}
#IBAction func addColorVariants(sender: AnyObject) {
additionalVariants++
let indexSet = NSMutableIndexSet()
indexSet.addIndex(additionalVariants)
//self.tableView.insertSections(NSIndexSet(indexesInRange: NSMakeRange(1, 5)), withRowAnimation: UITableViewRowAnimation.Automatic)
self.tableView.beginUpdates()
self.tableView.insertSections(indexSet, withRowAnimation: UITableViewRowAnimation.Automatic)
self.tableView.endUpdates()
//self.tableView.reloadSections(NSIndexSet(index: 0), withRowAnimation: UITableViewRowAnimation.Automatic)
//self.tableView.reloadData()
}

Resources