I have a UIViewController
holding one UITableView
with 3 sections. Also this UIViewController implements UISearchResultsUpdating
for searching in that UITableView
. The UIViewController
looks like this:
class MyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, UISearchResultsUpdating {
var searchController = UISearchController(searchResultsController: nil)
@IBOutlet weak var tableView: UITableView!
private struct TableViewSections {
static let sectionOne = 0
static let sectionTwo = 1
static let sectionThree = 2
}
override func viewDidLoad() {
super.viewDidLoad()
self.setupTableView()
self.setupSearchController()
}
private func setupSearchController() {
self.searchController.searchResultsUpdater = self
self.searchController.dimsBackgroundDuringPresentation = false
self.searchController.searchBar.placeholder = "Search"
self.searchController.searchBar.autocapitalizationType = .sentences
self.searchController.hidesNavigationBarDuringPresentation = false
if #available(iOS 11.0, *) {
self.navigationItem.searchController = self.searchController
} else {
self.tableView.tableHeaderView = self.searchController.searchBar
}
}
private func setupTableView() {
self.tableView.delegate = self
self.tableView.dataSource = self
}
func numberOfSections(in tableView: UITableView) -> Int {
return 3
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// In each of these cases i will do the work that is needed to return the correct number of rows
// Just for demonstration i return a random int in every case
if self.searchController.isActive {
switch section {
case TableViewSections.sectionOne:
return 10
case TableViewSections.sectionTwo:
return 20
case TableViewSections.sectionThree:
return 30
default:
return 0
}
} else {
switch section {
case TableViewSections.sectionOne:
return 15
case TableViewSections.sectionTwo:
return 25
case TableViewSections.sectionThree:
return 35
default:
return 0
}
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
// In each of these cases i will do the work that is needed to show the correct content
// Just for demonstration i return UITableViewCell() in every case
switch indexPath.section {
case TableViewSections.sectionOne:
if self.searchController.isActive {
return UITableViewCell()
} else {
return UITableViewCell()
}
case TableViewSections.sectionTwo:
if self.searchController.isActive {
return UITableViewCell()
} else {
return UITableViewCell()
}
case TableViewSections.sectionThree:
if self.searchController.isActive {
return UITableViewCell()
} else {
return UITableViewCell()
}
default:
return UITableViewCell()
}
}
// MARK: UISearchResultsUpdating
func updateSearchResults(for searchController: UISearchController) {
// do the search work here...
}
}
You can see the difference between tableView(_:numberOfRowsInSection:)
and cellForRow(at:)
.
My Question now is: What is recommended way to do so? Is the better solution tableView(_:numberOfRowsInSection:)
, cellForRow(at:)
or is there even a better solution?
1 Answer 1
The numberOfRowsInSection:
function looks good to me, changing this function to something better will depend on the possible logic you can apply in combination with the section parameter and your data. For example, if you have an array of dictionaries like this:
let data = [ 1: ["hola", "hey"], 2: ["adiós"] ]
The implementation of the method could be reduced to:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if self.searchController.isActive {
let array = searchData[section]
return array.count
}
let array = data[section]
return array.count
}
That indeed is valid for five sections and for one millions sections.
For the cellForRowAt indexPath:
function, I would put first the check for the searching status:
if self.searchController.isActive {
// handle sections for searching
} else {
// handle sections normaly
}
Doing it like this will allows you to, for example, group functionality shared across multiple sections. An implementation taking the example I put could be like:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let text = ""
if self.searchController.isActive {
let array = searchData[indexPath.section]
text = array[indexPath.row]
} else {
let array = data[indexPath.section]
text = array[indexPath.row]
}
configureCell(with: text, at: indexPath)
}
I did this because I prefer to not overload so much the implementation of the delegates. In configureCell:at:
you must implement the logic needed for the creation of the cells based in sections and the data you received.
As I wrote before, the implementation of those methods will depends on the structure of your data, it could reduce or increase the amount of code you need to show the data in the intended way.
-
3\$\begingroup\$ Welcome to Code Review! Thanks for contributing this nice post; I hope to see more of your contributions in future here on Code Review! \$\endgroup\$Toby Speight– Toby Speight2018年06月22日 09:52:03 +00:00Commented Jun 22, 2018 at 9:52
-
\$\begingroup\$ Thank you!, I hope I can keep contribute the community at least a little bit! \$\endgroup\$thxou– thxou2018年06月22日 09:55:24 +00:00Commented Jun 22, 2018 at 9:55
-
\$\begingroup\$ @thxou Thank you very much for your answer. I will think about my implementation. Hoping there are some more answers providing different solutions maybe :) \$\endgroup\$Teetz– Teetz2018年06月22日 11:29:19 +00:00Commented Jun 22, 2018 at 11:29