indexPath.row is out of range (always 0) - ios

I know this question has been asked multiple times already, but for some reason none of the responses I've read has solved this issue. I'm retrieving an array of MealObjects, a class that I have defined, and setting their properties to labels in a Table View with a defined limit of 5 Table View Cells. I only have one section in the Table View.
I've pasted my View Controller class below. I've marked the error in a comment. What am I doing wrong?
import UIKit
class PlateViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var titleLabel: UILabel!
#IBOutlet weak var subtitleLabel: UILabel!
let locationHelper = LocationHelper.sharedInstance
let userChoice = UserChoiceCollectionDataSource()
var mealArray: [MealObject] = []
override func viewDidLoad() {
super.viewDidLoad()
locationHelper.setupLocation()
locationHelper.callback = {
self.mealArray = self.userChoice.getUserSuggestions()
}
tableView.dataSource = self
tableView.delegate = self
tableView.estimatedRowHeight = 125
tableView.rowHeight = UITableViewAutomaticDimension
titleLabel.text = "Your Plate"
subtitleLabel.text = "The top 5 suggestions based on the information you provided"
navigationController?.hidesBarsOnSwipe = true
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MealCell", forIndexPath: indexPath) as! PlateTableViewCell
let meals = mealArray[indexPath.row]
/* FATAL ERROR: ARRAY INDEX OUT OF RANGE
INDEXPATH.ROW = 0
*/
print(indexPath.row)
cell.mealTitleLabel.text = meals.mealTitle
cell.descriptionLabel.text = meals.mealDescription
cell.priceLabel.text = "\(meals.priceValue)"
return cell
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
}

If index 0 is out of range, it looks like your array is empty at the time the cellForRowAtIndexPath accesses it. You hard coded the number of rows to 5, but you should only do that if the array has at least 5 elements in it. For example, in the numberOfRowsInSection you can do
return min(mealArray.count, 5)
I don't see where you are setting the array. Your code
locationHelper.callback = {
self.mealArray = self.userChoice.getUserSuggestions()
}
does not do that.

to have one section tableView you must do this:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mealArray.count
}
and after updating mealArray refresh tableView:
locationHelper.callback = {
self.mealArray = self.userChoice.getUserSuggestions()
dispatch_async(dispatch_get_main_queue()){
self.tableView.reloadData()
};
}

Related

Getting values of an array from another view controller to View Controller in tableViews labels swift ios

I am trying to get values from an array tempArray and descArray from AddViewController to a ViewViewController in a tableview in labels I want the both arrays values should be printed in temLabel and descLabel respectively but I am getting an error
Index out of range
whitout any issue I don't know what to do I have researched but nothing found helpful.
Here is the code for ViewViewController
import UIKit
class ViewViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var getAddController : AddViewController = AddViewController()
#IBOutlet weak var menu: UIBarButtonItem!//
#IBOutlet weak var viewTableView: UITableView!
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberRows = getAddController.tempArray
let numRow = String(describing: numberRows)
print (numRow)
return 10
//numRow.count
//getAddController.tempArray.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = viewTableView.dequeueReusableCell(withIdentifier: "ViewTableViewCell") as! ViewTableViewCell
let temarrayNow = getAddController.tempArray
let descArrayNow = getAddController.descArray
let crrentTemarray = String(describing: temarrayNow)
let decArNow = String(describing: descArrayNow)
cell.titleLabel.text = crrentTemarray[indexPath.row]
cell.descLabel.text = descArrayNow[indexPath.row]
return cell
}
override func viewDidLoad() {
super.viewDidLoad()
menu.target = revealViewController()
menu.action = #selector(SWRevealViewController.revealToggle(_:))
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
viewTableView.delegate = self
viewTableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
In numberOfRowsInSection return the tempArray count not the fixed value 10.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberRows = getAddController.tempArray
let numRow = String(describing: numberRows)
print (numRow)
return numberRows.count
//numRow.count
//getAddController.tempArray.count
}
This should work.
In this method, you need to return array count not the static value
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let numberRows = getAddController.tempArray
let numRow = String(describing: numberRows)
print (numRow)
return numberRows.count
}
And Note here that you are using two arrays getAddController.tempArray and getAddController.descArray in cellForRowAt and you are returning only one count in numberOfRowsInSection so you must have same no of elements in both array because if one of them is having less elements then you will get your error again
i want to know weather tempArray and descArray, both have same number of element(arr.count)
and in cellForRowAt,
let crrentTemarray = String(describing: temarrayNow)
let decArNow = String(describing: descArrayNow)
cell.titleLabel.text = crrentTemarray[indexPath.row] //here you have used crrentTemarray
cell.descLabel.text = descArrayNow[indexPath.row]
here you have used descArrayNow instead of decArNow (if I am not wrong)

How to Update UITableView With Swift?

I'm trying to populate a table view from a SQlite database. Tickets get printed in the console, but nothings shows up on the table view. What's the proper way to update and refresh? Here is my code. Thanks!
import UIKit
import SQLite
class TicketTableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var tickets = [String]()
#IBOutlet weak var table: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let dm = DatabaseManager.shared
let db = dm.db!
do {
for row in try db.prepare(dm.tickets) {
let ticket = row[dm.pick]
tickets.append(ticket)
debugPrint(ticket)
}
table.reloadData()
} catch {}
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tickets.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ticket")!
cell.detailTextLabel!.text = tickets[indexPath.row]
return cell
}
}
Dynamic table views needs to know their delegate and datasource. If you didn't set the delegate and datasource, you can add them programmatically in your viewDidLoad function. Like this:
override func viewDidLoad() {
super.viewDidLoad()
//Set delegate and datasource
table.delegate = self
table.dataSource = self
let dm = DatabaseManager.shared
let db = dm.db!
do {
for row in try db.prepare(dm.tickets) {
let ticket = row[dm.pick]
tickets.append(ticket)
debugPrint(ticket)
}
table.reloadData()
} catch {}
}

switch change on tableview row affecting other rows

I have the following class for a table view cell:
class QuestionCell: UITableViewCell {
#IBOutlet weak var questionAnswer: UISwitch!
#IBOutlet weak var questionLabel: UILabel!
var data: Answer?
func configureForQuestion(data: Answer) {
print("configureForQuestion triggered")
questionLabel.text = data.question
self.data = data
}
#IBAction func questionAnswerChanged(sender: UISwitch) {
data?.answer = sender.on
}
}
This cell consists of a label and switch. Currently when I change the switch status for the first row, it is also changing for the last row. No other rows seem to be connected in this way. I'm pretty stumped on this one.
Extra info:
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return answers.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(TableView.CellIdentifiers.QuestionCell, forIndexPath: indexPath) as! QuestionCell
cell.configureForQuestion(answers[indexPath.row])
return cell
}
You need to override prepareForReuse in the cell and reset you ui elements data.
override func prepareForReuse() {
self.questionLabel.text = nil
self.questionAnswer.on = data?.answer
}
It may be that your answers array does not have the data you expect. Print out the values of answers in the debugger and see if each answer has the indexPath.row you expect it to have.
Can you show your code for how you set the values in answers?

How to add a cell to my Table View dynamically using a button

I am trying to add a cell to my table view with a button. Everything I have read and watched suggests that what I have written should work, but it doesn't. Any suggestions?
import UIKit
class RootViewController: UITableViewController, UITableViewDataSource, UITableViewDelegate {
private var cellPointSize: CGFloat!
private var albumsList: AlbumList!
private var albums:[Album]!
private let albumCell = "Album"
#IBOutlet var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
let preferredTableViewFont = UIFont.preferredFontForTextStyle(UIFontTextStyleHeadline)
cellPointSize = preferredTableViewFont.pointSize
albumsList = AlbumList.sharedAlbumList
albums = albumsList.albums
self.myTableView.dataSource = self
self.myTableView.delegate = self
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}
// MARK: - Table view data source
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// Return the number of rows in the section.
return albums.count
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "Albums"
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = myTableView.dequeueReusableCellWithIdentifier(albumCell, forIndexPath: indexPath) as! UITableViewCell
//cell.textLabel?.font = fontForDisplay(atIndexPath: indexPath)
cell.textLabel?.text = albums[indexPath.row].name
cell.detailTextLabel?.text = albums[indexPath.row].artist
return cell
}
#IBAction func addNewAlbumAction(sender: UIBarButtonItem) {
var newAlbum = Album(nameIn: "New Title", yearIn: "New Year", artistIn: "New Artist", labelIn: "New Label")
albumsList.addAlbum(newAlbum)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.myTableView.reloadData()
})
}
func saveData(albumObject: Album) {
var archiveArray = NSMutableArray(capacity: albums.count)
for a in albums {
var albumEncodedObject = NSKeyedArchiver.archivedDataWithRootObject(a)
archiveArray.addObject(albumEncodedObject)
}
var userData = NSUserDefaults()
userData.setObject(archiveArray, forKey: "albums")
userData.synchronize()
}
My albums array is adding the data correctly. I can see the albums in the debugger. The delegate methods are never being called after the first time when the app loads. Any ideas?
in tableView:numberOfRowsInSection:, it returns albums.count
but when the button is pressed, you add the new album to albumsList
The problem is, albums will not get update.
So I think you should return albumsList.albums.count instead.
and in tableView:cellForRowAtIndexPath:, you modify the cell correspond to albumsList.albums[indexPath.row]

Display the same content in UITableView included in custom UITableViewCell

I have an issue with UITableView inserted in a UITableViewCell. This is my custom cell:
Everything works fine for the first, second and third cells, but from the fourth cell for the content of the UITableView inserted in cell is used always the same rows of the first, second and third cells alternatively. Instead the labels outside the UITableView are correctly displayed in every cells. This is my code for the custom cell class:
import UIKit
class SimulazioneQuestionTableViewCell: UITableViewCell, UITableViewDelegate, UITableViewDataSource {
let cellIdentifier = "simRisposteCell"
var arrayRisposte = [Risposta]()
#IBOutlet var numeroLabel: UILabel!
#IBOutlet var domandaLabel: UILabel!
#IBOutlet var risposteTableView: UITableView!
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
var nib = UINib(nibName: "SimulazioneQuestionAnswerTableViewCell", bundle: nil)
self.risposteTableView.registerNib(nib, forCellReuseIdentifier: self.cellIdentifier)
}
override func setSelected(selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayRisposte.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as SimulazioneQuestionAnswerTableViewCell
cell.textLabel?.text = arrayRisposte[indexPath.row].testo
return cell
}
}
And this is my view controller:
import UIKit
class SimulazioneViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
var areaSimulazione: Int = Int()
var arrayDomande = [Domanda]()
var arrayRisposte = [[Risposta]]()
var cellIdentifier = "domandaSimulazioneCell"
#IBOutlet var tempoTrascorso: UILabel!
#IBOutlet var simulazioneTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
arrayDomande = ModelManager.instance.getSimulazione(area: areaSimulazione)
var nib = UINib(nibName: "SimulazioneQuestionTableViewCell", bundle: nil)
self.simulazioneTableView.registerNib(nib, forCellReuseIdentifier: self.cellIdentifier)
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return arrayDomande.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: SimulazioneQuestionTableViewCell = simulazioneTableView.dequeueReusableCellWithIdentifier(self.cellIdentifier) as SimulazioneQuestionTableViewCell
var domanda: Domanda = arrayDomande[indexPath.row]
var rispXDomanda = ModelManager.instance.getAnswersForQuestion(domanda.numero)
cell.numeroLabel.text = String(domanda.numero)
cell.domandaLabel.text = domanda.testo
cell.arrayRisposte = rispXDomanda
return cell
}
Some advice to resolve this issue?
Thank you guys!
I never had the idea of putting a UITableView inside a UITableViewCell and therefore would have approached this from a different direction. I'm not saying the table inside a cell is impossible or a bad idea, but the following approach might actually be easier.
My understanding is that your table view shows a number of questions, and each question has possible answer right below it.
I'd use one section per question, with the first row using a custom cell that shows the numeroLabel and the domandaLabel (basically SimulazioneQuestionTableViewCell without the table), and then put the answers into the remaining rows for the section.
private let QuestionCellIdentifier = "QuestionCell"
private let AnswerCellIdentifier = "AnswerCell"
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return arrayDomande.count
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let rispXDomanda = ModelManager.instance.getAnswersForQuestion(domanda.numero)
return 1 + rispXDomanda.count // one extra for the question
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let domanda = arrayDomande[indexPath.section]
switch indexPath.row {
case 0:
let cell = tableView.dequeueReusableCellWithIdentifier(QuestionCellIdentifier) as SimulazioneQuestionTableViewCell
cell.numeroLabel.text = String(domanda.numero)
cell.domandaLabel.text = domanda.testo
return cell
default:
let cell = tableView.dequeueReusableCellWithIdentifier(AnswerCellIdentifier) as SimulazioneQuestionAnswerTableViewCell
let rispXDomanda = ModelManager.instance.getAnswersForQuestion(domanda.numero)
cell.textLabel?.text = rispXDomanda[indexPath.row - 1].testo
return cell
}
It might be a good idea to cache the answers that you get from ModelManager. It depends on how expensive it is to get them - with the code above getAnswersForQuestion() will be called a lot.
Edit: I personally like the .Grouped style for the table view. Using a section per question will nicely separate them visually.

Resources