Let's say I have an app that displays details for a person. Each person can have zero or more phone numbers and zero or more notes attached to it.
Thus, I have a Core Data entity Person
with one-to-many relationships to the Phone
entity and the Note
entity.
I want to show these in a UITableView
, where there is one section "phones" and one section "notes".
So, numberOfRowsInSection
would look like that:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0: // Phones
if self.person.phones.count > 0 {
return self.person.phones.count
} else {
return 1 // We want a different cell to display "no phones"
}
case 1: // Notes
if self.person.notes.count > 0 {
return self.person.notes.count
} else {
return 1 // We want a different cell to display "no notes"
default:
return 0
}
}
And cellForRowAt
would look like this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section {
case 0:
if self.person.phones.count > 0 {
return UITableViewCell.init(style: .default, reuseIdentifier: "PhoneNumberCell")
} else {
return UITableViewCell.init(style: .default, reuseIdentifier: "NoPhoneNumbersCell")
}
case 1:
if self.person.notes.count > 0 {
return UITableViewCell.init(style: .default, reuseIdentifier: "NoteCell")
} else {
return UITableViewCell.init(style: .default, reuseIdentifier: "NoNotesCell")
}
default:
return UITableViewCell.init(style: .default, reuseIdentifier: "default")
}
}
And then, you can guess already, the same code would repeat for willDisplay
:
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
switch indexPath.section {
case 0:
if self.person.phones.count > 0 {
// Configure Phone Cell
} else {
// Configure No Phones Cell
}
case 1:
if self.person.notes.count > 0 {
// Configure Note Cell
} else {
// Configure No Notes Cell
}
default: break
}
}
Same would apply for didSelectRowAt
(and other delegate/datasource methods) but I won't copy more here.
What I want to do is get rid of this long and repeating switch
with if..else
statement but the problem is that in some cases I use it to return a number, in others a String and in others to just configure a cell.
Does anyone have an idea or pattern to recommend for this case?
-
1Possible duplicate of Should I repeat condition checking code or put it in a function?gnat– gnat2016年10月11日 16:07:05 +00:00Commented Oct 11, 2016 at 16:07
-
see also: How to avoid spaghetti code when I have a lot of conditions?gnat– gnat2016年10月11日 16:22:18 +00:00Commented Oct 11, 2016 at 16:22
-
2This looks like an excellent opportunity for the "Replace conditionals with polymorphism" refactoring technique. Note that you're essentially managing 5 different table views, each could be their own class.amon– amon2016年10月12日 06:48:08 +00:00Commented Oct 12, 2016 at 6:48
1 Answer 1
Here's the kind of data source I would like to see for your example:
func numberOfSections(in tableView: UITableView) -> Int {
return sections.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return sections[section].itemCount
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
return sections[indexPath.section].cell(forRow: indexPath.row)
}
To make the above work, you will need a Section
Protocol:
protocol Section {
var itemCount: Int { get }
var cell(forRow row: Int) -> UITableViewCell
}
... as well as a PhoneNumberSection
and NoteSection
that both implement that protocol.
-
This is essentially what @amon was suggesting in his comment.Daniel T.– Daniel T.2017年05月08日 01:24:40 +00:00Commented May 8, 2017 at 1:24