Reputation: 13077
I am trying to build a table view for events, like so:
I have two cell prototypes:
Also, I have this class to represent a date:
class Event{
var name:String = ""
var date:NSDate? = nil
}
And this is the table controller:
class EventsController: UITableViewController {
//...
var eventsToday = [Event]()
var eventsTomorrow = [Event]()
var eventsNextWeek = [Event]()
override func viewDidLoad() {
super.viewDidLoad()
//...
self.fetchEvents()//Fetch events from server and put each event in the right property (today, tomorrow, next week)
//...
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let event = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
let seperator = tableView.dequeueReusableCellWithIdentifier("seperator", forIndexPath: indexPath) as SeperatorTableViewCell
//...
return cell
}
}
I have all the information I need at hand, but I can't figure out the right way to put it all together. The mechanics behind the dequeue func are unclear to me regrading multiple cell types.
I know the question's scope might seem a little too broad, but some lines of code to point out the right direction will be much appreciated. Also I think it will benefit a lot of users since I didn't found any Swift examples of this.
Thanks in advance!
Upvotes: 1
Views: 2730
Reputation: 438307
The basic approach is that you must implement numberOfRowsInSection
and cellForRowAtIndexPath
(and if your table has multiple sections, numberOfSectionsInTableView
, too). But each call to the cellForRowAtIndexPath
will create only one cell, so you have to do this programmatically, looking at the indexPath
to determine what type of cell it is. For example, to implement it like you suggested, it might look like:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return eventsToday.count + eventsTomorrow.count + eventsNextWeek.count + 3 // sum of the three array counts, plus 3 (one for each header)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var index = indexPath.row
// see if we're the "today" header
if index == 0 {
let separator = tableView.dequeueReusableCellWithIdentifier("separator", forIndexPath: indexPath) as SeparatorTableViewCell
// configure "today" header cell
return separator
}
// if not, adjust index and now see if we're one of the `eventsToday` items
index--
if index < eventsToday.count {
let eventCell = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
let event = eventsToday[index]
// configure "today" `eventCell` cell using `event`
return eventCell
}
// if not, adjust index and see if we're the "tomorrow" header
index -= eventsToday.count
if index == 0 {
let separator = tableView.dequeueReusableCellWithIdentifier("separator", forIndexPath: indexPath) as SeparatorTableViewCell
// configure "tomorrow" header cell
return separator
}
// if not, adjust index and now see if we're one of the `eventsTomorrow` items
index--
if index < eventsTomorrow.count {
let eventCell = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
let event = eventsTomorrow[index]
// configure "tomorrow" `eventCell` cell using `event`
return eventCell
}
// if not, adjust index and see if we're the "next week" header
index -= eventsTomorrow.count
if index == 0 {
let separator = tableView.dequeueReusableCellWithIdentifier("separator", forIndexPath: indexPath) as SeparatorTableViewCell
// configure "next week" header cell
return separator
}
// if not, adjust index and now see if we're one of the `eventsToday` items
index--
assert (index < eventsNextWeek.count, "Whoops; something wrong; `indexPath.row` is too large")
let eventCell = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
let event = eventsNextWeek[index]
// configure "next week" `eventCell` cell using `event`
return eventCell
}
Having said that, I really don't like that logic. I'd rather represent the "today", "tomorrow" and "next week" separator cells as headers, and use the section logic that table views have.
For example, rather than representing your table as a single table with 8 rows in it, you could implement that as a table with three sections, with 2, 1, and 2 items in each, respectively. That would look like:
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 3
}
override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
switch section {
case 0:
return "Today"
case 1:
return "Tomorrow"
case 2:
return "Next week"
default:
return nil
}
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return eventsToday.count
case 1:
return eventsTomorrow.count
case 2:
return eventsNextWeek.count
default:
return 0
}
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let eventCell = tableView.dequeueReusableCellWithIdentifier("event", forIndexPath: indexPath) as EventTableViewCell
var event: Event!
switch indexPath.section {
case 0:
event = eventsToday[indexPath.row]
case 1:
event = eventsTomorrow[indexPath.row]
case 2:
event = eventsNextWeek[indexPath.row]
default:
event = nil
}
// populate eventCell on the basis of `event` here
return eventCell
}
The multiple section approach maps more logically from the table view to your underlying model, so I'd to adopt that pattern, but you have both approaches and you can decide.
Upvotes: 4