I'm having problems with custom cells in a tableView. What I am doing is, I access my Database and for every row in the Database, I will fill one cell. The problem is, it only prints the last row over and over for each cell; Then, I tried using the filter (by ID), but it only prints the first row for every cell.
I thank in advance for your help. My code is the following
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int{
var x = Int()
do{
let count = try conn.db!.scalar(tblPersona.count)
x = count
}
catch{
}
return x
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! consultaInformacionTableViewCell
var res = Int64 (1)
do{
let query = tblPersona.filter(id == res)
for custom in try conn.db!.prepare(query){
print(custom[nombre]!)
cell.nombreLabel.text = custom[nombre]!
cell.emailLabel.text = custom[email]!
res = res + 1
}
}
catch{
}
return cell
}
When I print "custom[nombre]!" it prints all the rows each time one loop is completed.
Your filter criteria appears to be incorrect.
tblPersona.filter(id == res)
The comparison is resolving first. So, you are effectively saying:
tblPersona.filter(true)
It appears that "true" is returning every entry. However, without knowing about the type of filter you're using, I can't provide a working example.
Related
I have a messaging function in an app and I'm trying to sort the cells of a tableView based off a timestamp included with every message. I have two strings that are used to populate two different custom cells. Due to this, I can't just rearrange the contents of the string, I need to rearrange the rows themselves.
class messageThreadViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 {
return finalItems.count
} else {
return finalItems2.count
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "message1") as! MyTableViewCellThread
cell.messagelabel?.text = finalItems[indexPath.row]
return cell
} else {
let cell2 = tableView.dequeueReusableCell(withIdentifier: "message2") as! MyTableViewCellThread2
cell2.messageLabel2?.text = finalItems2[indexPath.row]
return cell2
}
}
the two different strings are finalItems and finalitems2. I have attached an image of the app running to help get a better idea of what I'm trying to do. Let me know if I can explain it better. Thanks in advance!
In general, when sorting cells in tableviews, you simply sort the arrays (datasource) themselves and then call the tableView.reloadData() method.
To sort arrays, the best practice is
let sortedArray = array.filter { $0.timestamp > $1.timestamp }
// This sort with descending order, just replace '>' with '<'
if realm.objects(Accomp2.self).count > 0 {
for acc in accompData {
models.removeAll()
if acc.month == date4 {
models.append(acc)
//print(models)
count += 1
}
}
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return models.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = models[indexPath.row].title
let date = models[indexPath.row].date
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd, YYYY"
cell.detailTextLabel?.text = formatter.string(from: date!)
return cell
}
In the first block of code, I add the objects that have the date from this month from the Realm Database to an array called models. When I print out models, I can see all the objects and their properties.
Then, in these two tableView functions, I return the number of objects in models, but the count only returns "1." I tried to use the count variable and return that instead, but it then gives me an error in the cellForRowAt function saying that those objects do not exist. I am not sure why I can only access one of the objects from models which I declare before the viewDidLoad() method as var models = [Accomp2]() Should I be using another tableView method, or is there a way to access all of the objects and create tableView cells for all of them?
As of the removeAll clears the array every cycle leaving only the last item , you see all in console as you print them individually
for acc in accompData {
models.removeAll()
i guess it could be
models.removeAll()
for acc in accompData {
The answer to the question was moving the models.removeAll() out of the array.
I have error says "Thread 1: Fatal error: Index out of range".
on
cell.titleLabel.text = cellDataArrayPoster[indexPath.row].jobTitlePoster as? String
please notice that I'm using two different cells,
as prototypeCells. Moreover, they both have different identifier.
both arrays have getting their data from firebase.
var cellDataArray = [cellData]()
var cellDataArrayPoster = [cellDataPoster]()
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return cellDataArray.count + cellDataArrayPoster.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let index = indexPath.row
if index == 0 {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! infoCell
cell.titleLabel.text = cellDataArray[indexPath.row].jobTitle as? String
cell.companyLabel.text = cellDataArray[indexPath.row].companyName
//cell.timeStampLabel.text = cellDataArray[indexPath.row].createdAt.calenderTimeSinceNow()
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cellPoster", for: indexPath) as! infoCellPoster
cell.titleLabel.text = cellDataArrayPoster[indexPath.row].jobTitlePoster as? String
//cell.timeStampLabel.text = cellDataArray[indexPath.row].createdAt.calenderTimeSinceNow()
return cell
}
}
You misunderstood the concept of table view delegate methods. It is good to read more from the documentation.
My general rule of thumb is to always use only 1 array as data source for table view to avoid index out of range situations.
In your particular case the error is saying all about it - you are trying to reach index number that is out of range of the array. The easiest workaround will be to combine the two arrays in one, and have some sort of inheritance between the objects so they can fit.
I am using a dictionary in order to fill a tableview.
Trying to appear only cells that have a certain userID, but it return also the cells that doesn't have this userID.
I have managed to count only the items from dictionary with the certain userID and if for example my dictionary has 8 entries and I need to show only the last 2 entries which have different userID, it returns 2 empty cells (which are the first 2 in the dictionary.
How I can get only the cells with the certain userID?
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
var returnCount:Int = 0
let currentUserId = NSUserDefaults.standardUserDefaults().stringForKey("userId")
for place in places {
if place["userID"] == currentUserId {
returnCount++
}
}
return returnCount
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
let currentUserId = NSUserDefaults.standardUserDefaults().stringForKey("userId")
let currentPlacesUserId = places[indexPath.row]["userID"]
if currentPlacesUserId == currentUserId {
cell.textLabel!.text = places[indexPath.row]["name"]
cell.detailTextLabel?.text = places[indexPath.row]["issue"]
}
return cell
}
The fact is that you should not do this kind of logic inside de tableView delegate methods. Try getting the places from that userId when you load this array.
If you really want to proceed with the approach you are currently using try the following:
Not sure if this gonna work, but you are creating the cell even if it doesnt have the user Id you want. Try this:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let currentUserId = NSUserDefaults.standardUserDefaults().stringForKey("userId")
let currentPlacesUserId = places[indexPath.row]["userID"]
if currentPlacesUserId == currentUserId {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.textLabel!.text = places[indexPath.row]["name"]
cell.detailTextLabel?.text = places[indexPath.row]["issue"]
return cell
} else{
return nil
}
}
How to show the data thats decks.status == true, and ignore those objects set to false?
data:
var decks: [DeckOfCards]
What I've got now:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as TableViewCell
if (thedeck.decks[indexPath.row].status == true) {
cell.label.text = "\(thedeck.decks[indexPath.row].card.name)"
}
}
You're going about this the wrong way. By the time you get to cellForRowAtIndexPath, you're already stated that a cell should be dequeued for this index path (and therefore at this index in your data array). The right place to be doing this filtering is in your data source.
For example, in addition to your decks array, you could make a computed property (filteredDecks) that gets its value by filtering the decks array.
var decks = [DeckOfCards]
var filteredDecks: [DeckOfCards] {
return decks.filter { $0.status }
}
You can then use this property as the data source for your table view.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredDecks.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
cell.label.text = "\(filteredDecks[indexPath.row].card.name)"
return cell
}
Now since this solution computes the filteredDecks array on each property access, it may not be the best approach if decks is a large array, or if you're reloading the table view frequently. If this is the case, and it's possible to do so, you should prefer filtering the decks array ahead of time using the same method shown in the computed property above.
You could use the filter function on the decks
let filteredDecks = decks.filter({$0.status})
Filter your array as
self.decks = self.decks.filter {
(d: DeckOfCards) -> Bool in
return d.status == true
}
Now your array will have the filtered values. you dont need to check for status inside cellForRowAtIndexPath function.