How to show only filtered data in a table - swift

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.

Related

Reusable UITableView for Varying Data Input - Swift/Xcode

I have a TableView that I want to reuse for different categories of data (essentially as plugins.. the tableView being a skeleton and being filled with whatever I want it to be). The TableView is filled with different categories of data (and related actions) depending on essentially what ViewController the user came from. I understand how to make it display the various data (just send it whatever array I want it to display), but I can't figure out how I could control the actions for the data at the specific index selected in didSelectRowAtIndexPath.
How could I do this? How could I create an array that has both Strings and executable actions associated with each indices? For example:
arrayOneNames = ["Tigris", "Leo", "Barko"]
arrayOneActions = [displayTheTigers, displayTheCats, displayTheDogs]
If "Leo" is selected in the tableView, then "displayTheCats" is executed. Again, I want each array to be a separate Class that I can use as a plugin, so that I can fill the tableView with whichever Class of data I want it to display and execute, depending on which ViewController the user came from previously. Please answer in Swift.
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell=UITableViewCell() // cell you've created
cell.data = arrayOneNames[indexPath.row] // passing relevant data
cell.tag = indexPath.row // the tag you want to pass for particular data
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath)! as UITableViewCell // take selected cell
if(cell.tag == 0) { // call relevant function accordingly.
self.tigrisTouch()
} else if (cell.tag == 1) {
self.leoTouch()
} else {
self.barkoTouch()
}
}
private func tigrisTouch() {
}
private func leoTouch() {
}
private func barkoTouch() {
}

Is there any way to sort rows of a table view using a date/time? swift

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 '<'

Index out of range for indexPath swift

I am making an app that requires the user to tap on multiple cells in order to select them. when they tap on a cell, a .Checkmark accessory item will appear. For some reason though whenever I try and get to that VC the app crashes and I get the following error messages on line 8(if !checked[indexPath.row]):
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell: InstrumentTableCell! = tableView.dequeueReusableCellWithIdentifier(identifier) as? InstrumentTableCell
cell.configurateTheCell(recipies[indexPath.row])
if !checked[indexPath.row] {
cell.accessoryType = .None
} else if checked[indexPath.row] {
cell.accessoryType = .Checkmark
}
return cell
}
and this is my working checked method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
{
tableView.deselectRowAtIndexPath(indexPath, animated: true)
if let cell = tableView.cellForRowAtIndexPath(indexPath) {
if cell.accessoryType == .Checkmark {
cell.accessoryType = .None
checked[indexPath.row] = false
} else {
cell.accessoryType = .Checkmark
checked[indexPath.row] = true
}
}
}
Your problem is that you only store items in your checked array when tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) is called. However, that method is only called when you actually select a row.
tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) on the other hand is called each and every time you need to render a new table cell.
So when you in cellForRowAtIndexPath asks:
if !checked[indexPath.row]
then you can not be sure that checked actually contains anything. For instance the first time you start rendering your cells, your checked array does not contain any values and therefore it crashes when you ask it for a value on a position that it does not have a value for.
One solution could be to initialize your checked array to contain all false values. I'm guessing you have some model array called recipies so you could do something like:
for (index, _) in recipies.enumerate() {
checked.append(false)
}
Or as #AaronBrager suggests in the comments below (which is way prettier :))
checked = Array(count:recipies.count, repeatedValue:false)
that way you are sure that your checked array is properly initialized with the same number of elements as you have recipies.
Another option could be to let the individual elements in recipies know whether or not they are checked.
Hope this makes sense and helps you.

How I can show only certain cells taken from Dictionary in a tableView in Swift

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
}
}

Swift TableView with multiple prototype cells are not displaying all Rows

I have a UITableView created with 2 prototype cells, each of which have separate identifiers and subclasses.
My problem is when I display the cells the second prototype's first row gets absorbed under the first prototype cell.
For example, I have the first prototype cell displaying only 1 item. The second prototype cell should display 4 items. But, the first item from the second prototype cell is not displaying and, instead, there are only 3 of the four items visible.
Here is the code I have implemented:
override func viewDidLoad() {
super.viewDidLoad()
self.staticObjects.addObject("Please...")
self.objects.addObject("Help")
self.objects.addObject("Me")
self.objects.addObject("Thank")
self.objects.addObject("You")
self.tableView.reloadData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.objects.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
if(indexPath.row==0){
let staticCell = self.tableView.dequeueReusableCellWithIdentifier("staticCell", forIndexPath: indexPath) as! StaticTableViewCell
staticCell.staticTitleLabel.text = self.staticObjects.objectAtIndex(indexPath.row) as? String
return staticCell
}
else{
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! PillarTableViewCell
cell.titleLabel.text = self.objects.objectAtIndex(indexPath.row) as? String
return cell
}
Thanks for all the help.
You have logic issues with how you are counting the number of rows in your table for both tableView:numberOfRowsInSection and tableView:cellForRowAtIndexPath.
Your code is producing a display output as shown below where:
The blue cells represent your staticCell prototype table cell view; these are the values from the staticsObjects array.
The yellow cells represent your cell prototype table cell view; these are the values from the objects array.
1. Look at tableView:cellForRowAtIndexPath:
In the cellForRowAtIndexPath method, you are only returning the count of the objects array.
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.objects.count
}
That means that the number of rows you will have in your table will be 4 instead of 5. Instead, you want to return the sum of the two arrays you are using in your table: objects.count + staticObjects.count. For example:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objects.count + staticObjects.count
}
2. Look at tableView:cellForRowAtIndexPath:
Here's your original code with my comments..
// You are assuming that `staticObjects` will always have
// exactly one row. It's better practice to make this
// calculation more dynamic in case the array size changes.
if (indexPath.row==0){
let staticCell = self.tableView.dequeueReusableCellWithIdentifier("staticCell", forIndexPath: indexPath) as! StaticTableViewCell
staticCell.staticTitleLabel.text = self.staticObjects.objectAtIndex(indexPath.row) as? String
return staticCell
} else {
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! PillarTableViewCell
// Here's your problem! You need to calculate the row
// because you want to put the objects from your other
// array first. As a result, you need to account for them.
cell.titleLabel.text = self.objects.objectAtIndex(indexPath.row) as? String
return cell
}
Now, here's one way to fix your errors stated in the above discussion:
// If the indexPath.row is within the size range of staticObjects
// then display the cell as a "staticCell".
// Notice the use of "staticObjects.count" in the calculation.
if indexPath.row < staticObjects.count {
let staticCell = self.tableView.dequeueReusableCellWithIdentifier("staticCell", forIndexPath: indexPath) as! StaticTableViewCell
staticCell.staticTitleLabel.text = self.staticObjects.objectAtIndex(indexPath.row) as? String
return staticCell
} else {
// If you get here then you know that your indexPath.row is
// greater than the size of staticObjects. Now you want to
// display your objects values.
// You must calculate your row value. You CANNOT use the
// indexPath.row value because it does not directly translate
// to the objects array since you put the staticObjects ahead
// of them. As a result, subtract the size of the staticObjects
// from the indexPath.row.
let row = indexPath.row - staticObjects.count
let cell = self.tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! PillarTableViewCell
cell.titleLabel.text = self.objects.objectAtIndex(row) as? String
return cell
}
Now you should see this: