Reputation: 1219
In a UITableViewController
class that is responsible to populate a TableView
with cells, I want to filter some of the cells but I can't figure out how I would do that.
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return meals.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "MealTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MealTableViewCell
let meal = meals[indexPath.row]
cell.title.text = meal.name
return cell
}
I have the attribute meal.veg : Bool
and ideally, I want to populate the TableView
only with meals that are meal.veg == true
. What I am stuck with, is that I can't really understand how the override func tableView()
does populate the table. I mean how is this function called? And how can I filter the cells.
Because the return type of the function is UITableViewCell
and is not Optional, I have to return a cell, which doesn't allow the chance for cell filtering.
Upvotes: 1
Views: 2769
Reputation: 2856
Here's an example, with some extra suggestions :)
OK so let's say you've got a class, something like:
class Meal {
var name: String
var veg: Bool
init(_ name: String, isVeg veg: Bool) {
self.name = name
self.veg = veg
}
}
Then you have a load of meals in an array:
let carrots = Meal("Carrots", isVeg: true)
let steak = Meal("Steak & chips", isVeg: false)
let curry = Meal("Chicken korma", isVeg: false)
let sausage = Meal("Bangers and mash!", isVeg: false)
let salad = Meal("Spring asparagus salad", isVeg: true)
let meals = [carrots, steak, curry, sausage, salad]
What you want to do is filter those meals (based on whatever parameter you like).
var isFiltered = true
var filteredMeals = meals.filter { $0.veg }
Here you can see I've added a value (isFiltered
) for whether you want to use the filter or not. (We'll see more why in a sec). Note also that the filteredMeals is in a separate array so that we don't lose our original data source, should we wish to remove or change the filter.
With your tableView
you'd then set the numberOfRowsInSection
and cellForRowAtIndexPath
:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return isFiltered ? filteredMeals.count : meals.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "MealTableViewCell"
let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! MealTableViewCell
let meal = isFiltered ? filteredMeals[indexPath.row] ? meals[indexPath.row]
cell.title.text = meal.name
return cell
}
So here you can see I'm making use of the isFiltered
variable to determine whether I want to use the filtered results or not.
Upvotes: 4
Reputation: 53
Why don't you just filter the meals(outside of table view's datasource method) instead of cells and use the count of the filtered meals accordingly? Like:
let filteredMeals = meals.filter { $0.veg == true }
Upvotes: 1
Reputation: 11127
Problem - With UITableView
, the method which is fired first is numberOfRowsInSection
this will tell the TableView how many cell are there in the TableView
, after getting the numbers the method cellForRowAtIndexPath
will get fired which will used to design your TableView
.
Now you are returning a count lets say 10 in numberOfRowsInSection
out of which you just want to show 5 cell lets say those 5 are meal.veg == true
, based on your filter meal.veg which is not possible as you need to return a cell from cellForRowAtIndexPath
.
Solution - To resolve it, before reloading your table view you need to filter your array and filter out those results which are having a value meal.veg == true
, then you need to pass the count of your filtered array in numberOfRowsInSection
and as per that filtered array you can design your cell from cellForRowAtIndexPath
Upvotes: 2