Skip to content

Commit a1a86c8

Browse files
author
Jurvis Tan
committed
paging by date on EventsListViewController
1 parent 7095e2f commit a1a86c8

10 files changed

+388
-170
lines changed

FOSSAsia.xcodeproj/project.pbxproj

+12-4
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
280DE5291C62FCB200E64020 /* Podfile in Resources */ = {isa = PBXBuildFile; fileRef = 280DE5281C62FCB200E64020 /* Podfile */; };
1616
281324ED1C672EE80072E730 /* EventsResultsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 281324EC1C672EE80072E730 /* EventsResultsViewController.swift */; };
1717
281324EF1C6735430072E730 /* EventsBaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 281324EE1C6735430072E730 /* EventsBaseViewController.swift */; };
18+
28352C711C6B2B9F0055E2F5 /* EventsListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28352C701C6B2B9F0055E2F5 /* EventsListViewController.swift */; };
19+
28352C731C6B411C0055E2F5 /* EventsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28352C721C6B411C0055E2F5 /* EventsListViewModel.swift */; };
1820
2859B0FE1C5DF8D7009656FC /* EventViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2859B0FD1C5DF8D7009656FC /* EventViewController.swift */; };
1921
2859B1001C5DFD09009656FC /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2859B0FF1C5DFD09009656FC /* Colors.swift */; };
2022
2859B1021C5E29DC009656FC /* EventInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2859B1011C5E29DC009656FC /* EventInfoView.swift */; };
2123
285F1B651C5AF7B100A7E0DD /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285F1B641C5AF7B100A7E0DD /* AppDelegate.swift */; };
22-
285F1B671C5AF7B100A7E0DD /* EventsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285F1B661C5AF7B100A7E0DD /* EventsViewController.swift */; };
24+
285F1B671C5AF7B100A7E0DD /* ScheduleViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285F1B661C5AF7B100A7E0DD /* ScheduleViewController.swift */; };
2325
285F1B691C5AF7B100A7E0DD /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 285F1B681C5AF7B100A7E0DD /* SecondViewController.swift */; };
2426
285F1B6C1C5AF7B100A7E0DD /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 285F1B6A1C5AF7B100A7E0DD /* Main.storyboard */; };
2527
285F1B6E1C5AF7B100A7E0DD /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 285F1B6D1C5AF7B100A7E0DD /* Assets.xcassets */; };
@@ -63,12 +65,14 @@
6365
280DE5281C62FCB200E64020 /* Podfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Podfile; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
6466
281324EC1C672EE80072E730 /* EventsResultsViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventsResultsViewController.swift; sourceTree = "<group>"; };
6567
281324EE1C6735430072E730 /* EventsBaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventsBaseViewController.swift; sourceTree = "<group>"; };
68+
28352C701C6B2B9F0055E2F5 /* EventsListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventsListViewController.swift; sourceTree = "<group>"; };
69+
28352C721C6B411C0055E2F5 /* EventsListViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventsListViewModel.swift; sourceTree = "<group>"; };
6670
2859B0FD1C5DF8D7009656FC /* EventViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventViewController.swift; sourceTree = "<group>"; };
6771
2859B0FF1C5DFD09009656FC /* Colors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Colors.swift; sourceTree = "<group>"; };
6872
2859B1011C5E29DC009656FC /* EventInfoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventInfoView.swift; sourceTree = "<group>"; };
6973
285F1B611C5AF7B100A7E0DD /* FOSSAsia.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = FOSSAsia.app; sourceTree = BUILT_PRODUCTS_DIR; };
7074
285F1B641C5AF7B100A7E0DD /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
71-
285F1B661C5AF7B100A7E0DD /* EventsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventsViewController.swift; sourceTree = "<group>"; };
75+
285F1B661C5AF7B100A7E0DD /* ScheduleViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScheduleViewController.swift; sourceTree = "<group>"; };
7276
285F1B681C5AF7B100A7E0DD /* SecondViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = "<group>"; };
7377
285F1B6B1C5AF7B100A7E0DD /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
7478
285F1B6D1C5AF7B100A7E0DD /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
@@ -132,7 +136,8 @@
132136
isa = PBXGroup;
133137
children = (
134138
281324EE1C6735430072E730 /* EventsBaseViewController.swift */,
135-
285F1B661C5AF7B100A7E0DD /* EventsViewController.swift */,
139+
285F1B661C5AF7B100A7E0DD /* ScheduleViewController.swift */,
140+
28352C701C6B2B9F0055E2F5 /* EventsListViewController.swift */,
136141
281324EC1C672EE80072E730 /* EventsResultsViewController.swift */,
137142
2859B0FD1C5DF8D7009656FC /* EventViewController.swift */,
138143
28091E8C1C6332A600E789CD /* FilterListViewController.swift */,
@@ -156,6 +161,7 @@
156161
children = (
157162
285F1BBB1C5B2B2B00A7E0DD /* EventViewModel.swift */,
158163
28A0BC911C5F4FEC001625FB /* ScheduleViewModel.swift */,
164+
28352C721C6B411C0055E2F5 /* EventsListViewModel.swift */,
159165
285F1BB31C5B0FDC00A7E0DD /* SpeakerViewModel.swift */,
160166
28091E9A1C64CC1A00E789CD /* TrackViewModel.swift */,
161167
);
@@ -420,16 +426,18 @@
420426
285F1B691C5AF7B100A7E0DD /* SecondViewController.swift in Sources */,
421427
281324EF1C6735430072E730 /* EventsBaseViewController.swift in Sources */,
422428
28091E8D1C6332A600E789CD /* FilterListViewController.swift in Sources */,
429+
28352C731C6B411C0055E2F5 /* EventsListViewModel.swift in Sources */,
423430
2859B1001C5DFD09009656FC /* Colors.swift in Sources */,
424431
285F1B8F1C5B069D00A7E0DD /* Observable.swift in Sources */,
432+
28352C711C6B2B9F0055E2F5 /* EventsListViewController.swift in Sources */,
425433
285F1BBC1C5B2B2B00A7E0DD /* EventViewModel.swift in Sources */,
426434
285F1B651C5AF7B100A7E0DD /* AppDelegate.swift in Sources */,
427435
2859B0FE1C5DF8D7009656FC /* EventViewController.swift in Sources */,
428436
2859B1021C5E29DC009656FC /* EventInfoView.swift in Sources */,
429437
28091E9B1C64CC1A00E789CD /* TrackViewModel.swift in Sources */,
430438
285F1BB81C5B16C700A7E0DD /* EventCell.swift in Sources */,
431439
28091E8F1C636F9000E789CD /* FilterCell.swift in Sources */,
432-
285F1B671C5AF7B100A7E0DD /* EventsViewController.swift in Sources */,
440+
285F1B671C5AF7B100A7E0DD /* ScheduleViewController.swift in Sources */,
433441
28091E911C64B0B900E789CD /* Constants.swift in Sources */,
434442
281324ED1C672EE80072E730 /* EventsResultsViewController.swift in Sources */,
435443
285F1B921C5B06CC00A7E0DD /* Event.swift in Sources */,

FOSSAsia/EventsBaseViewController.swift

+7-8
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import UIKit
1010

1111
class EventsBaseViewController: UIViewController {
12-
let kEventCellReuseIdentifier = "EventCell"
12+
static let kEventCellReuseIdentifier = "EventCell"
1313
var allEvents: [EventViewModel] = []
1414

1515
@IBOutlet weak var tableView: UITableView!
@@ -19,7 +19,9 @@ class EventsBaseViewController: UIViewController {
1919
viewModel?.events.observe {
2020
[unowned self] in
2121
self.allEvents = $0
22-
self.tableView.reloadData()
22+
if self.tableView != nil {
23+
self.tableView.reloadData()
24+
}
2325
}
2426
}
2527
}
@@ -34,6 +36,7 @@ class EventsBaseViewController: UIViewController {
3436
// Do any additional setup after loading the view, typically from a nib.
3537
tableView.dataSource = self
3638
tableView.delegate = self
39+
tableView.rowHeight = 70
3740
}
3841

3942
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
@@ -57,7 +60,7 @@ extension EventsBaseViewController: UITableViewDelegate {
5760

5861
extension EventsBaseViewController: UITableViewDataSource {
5962
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
60-
let cell = tableView.dequeueReusableCellWithIdentifier(kEventCellReuseIdentifier, forIndexPath: indexPath) as! EventCell
63+
let cell = tableView.dequeueReusableCellWithIdentifier(EventsBaseViewController.kEventCellReuseIdentifier, forIndexPath: indexPath) as! EventCell
6164
let eventViewModel = allEvents[indexPath.row]
6265
cell.configure(withPresenter: eventViewModel)
6366

@@ -71,9 +74,5 @@ extension EventsBaseViewController: UITableViewDataSource {
7174
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
7275
tableView.deselectRowAtIndexPath(indexPath, animated: true)
7376
}
74-
75-
func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
76-
// have to patch in code because IB wasn't listening to me
77-
return 70
78-
}
77+
7978
}
+143
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
//
2+
// EventsListViewController.swift
3+
// FOSSAsia
4+
//
5+
// Created by Jurvis Tan on 10/2/16.
6+
// Copyright © 2016 FossAsia. All rights reserved.
7+
//
8+
9+
import UIKit
10+
11+
class EventsListViewController: UIViewController {
12+
weak var pageVC: UIPageViewController!
13+
private (set) var scheduleViewControllers: [ScheduleViewController]!
14+
var viewModel: EventsListViewModel? {
15+
didSet {
16+
viewModel?.allSchedules.observe { viewModels in
17+
let viewControllers = viewModels.map { viewModel in
18+
return ScheduleViewController.scheduleViewControllerFor(viewModel)
19+
}
20+
self.scheduleViewControllers = viewControllers
21+
if let firstVC = self.scheduleViewControllers.first {
22+
self.pageVC.setViewControllers([firstVC], direction: .Forward, animated: true, completion: { (done) -> Void in
23+
if done {
24+
self.currentViewController = firstVC
25+
}
26+
})
27+
}
28+
}
29+
}
30+
}
31+
32+
var currentViewController: ScheduleViewController!
33+
var searchController: UISearchController!
34+
var resultsTableController: EventsResultsViewController!
35+
var filterString: String? = nil {
36+
didSet {
37+
currentViewController.filterString = filterString
38+
resultsTableController.visibleEvents = currentViewController.filteredEvents
39+
resultsTableController.tableView.reloadData()
40+
}
41+
}
42+
43+
override func viewDidLoad() {
44+
super.viewDidLoad()
45+
46+
viewModel = EventsListViewModel()
47+
48+
resultsTableController = storyboard!.instantiateViewControllerWithIdentifier(EventsResultsViewController.StoryboardConstants.viewControllerId) as! EventsResultsViewController
49+
resultsTableController.allEvents = currentViewController.allEvents
50+
51+
searchController = UISearchController(searchResultsController: resultsTableController)
52+
searchController.searchResultsUpdater = self
53+
searchController.hidesNavigationBarDuringPresentation = false
54+
55+
// We want to be the delegate for our filtered table so didSelectRowAtIndexPath(_:) is called for both tables.
56+
// calling .view will force Storyboards to render the view hierarchy to make tableView accessible
57+
let _ = searchController.view
58+
resultsTableController.tableView.delegate = self
59+
60+
searchController.searchBar.searchBarStyle = .Minimal
61+
searchController.searchBar.tintColor = Colors.creamTintColor
62+
searchController.searchBar.placeholder = "Search"
63+
if let textFieldInSearchBar = searchController.searchBar.valueForKey("searchField") as? UITextField {
64+
textFieldInSearchBar.textColor = Colors.creamTintColor
65+
}
66+
67+
navigationItem.titleView = searchController.searchBar
68+
69+
definesPresentationContext = true
70+
}
71+
72+
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
73+
if (segue.identifier == "EventsPageViewController") {
74+
if let embeddedPageVC = segue.destinationViewController as? UIPageViewController {
75+
self.pageVC = embeddedPageVC
76+
self.pageVC.dataSource = self
77+
}
78+
}
79+
}
80+
}
81+
82+
83+
extension EventsListViewController: UISearchResultsUpdating {
84+
func updateSearchResultsForSearchController(searchController: UISearchController) {
85+
guard searchController.active else {
86+
return
87+
}
88+
filterString = searchController.searchBar.text
89+
}
90+
}
91+
92+
extension EventsListViewController: UITableViewDelegate {
93+
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
94+
let selectedEventViewModel: EventViewModel
95+
96+
selectedEventViewModel = resultsTableController.visibleEvents[indexPath.row]
97+
98+
let eventViewController = EventViewController.eventViewControllerForEvent(selectedEventViewModel)
99+
100+
navigationController?.pushViewController(eventViewController, animated: true)
101+
}
102+
103+
}
104+
105+
extension EventsListViewController: UIPageViewControllerDataSource {
106+
func pageViewController(pageViewController: UIPageViewController, viewControllerBeforeViewController viewController: UIViewController) -> UIViewController? {
107+
guard let scheduleViewController = viewController as? ScheduleViewController else {
108+
return nil
109+
}
110+
111+
guard let viewControllerIndex = scheduleViewControllers.indexOf(scheduleViewController) else {
112+
return nil
113+
}
114+
115+
let previousIndex = viewControllerIndex - 1
116+
guard previousIndex >= 0 &&
117+
scheduleViewControllers.count > previousIndex else {
118+
return nil
119+
}
120+
self.currentViewController = scheduleViewControllers[previousIndex]
121+
return scheduleViewControllers[previousIndex]
122+
}
123+
124+
func pageViewController(pageViewController: UIPageViewController, viewControllerAfterViewController viewController: UIViewController) -> UIViewController? {
125+
guard let scheduleViewController = viewController as? ScheduleViewController else {
126+
return nil
127+
}
128+
129+
guard let viewControllerIndex = scheduleViewControllers.indexOf(scheduleViewController) else {
130+
return nil
131+
}
132+
133+
let nextIndex = viewControllerIndex + 1
134+
let viewControllersCount = scheduleViewControllers.count
135+
guard viewControllersCount != nextIndex &&
136+
viewControllersCount > nextIndex else {
137+
return nil
138+
}
139+
self.currentViewController = scheduleViewControllers[nextIndex]
140+
return scheduleViewControllers[nextIndex]
141+
142+
}
143+
}

FOSSAsia/EventsListViewModel.swift

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
//
2+
// EventsListViewModel.swift
3+
// FOSSAsia
4+
//
5+
// Created by Jurvis Tan on 10/2/16.
6+
// Copyright © 2016 FossAsia. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
struct EventsListViewModel {
12+
//MARK:- Properties
13+
let allSchedules: Observable<[ScheduleViewModel]>
14+
let count: Observable<Int>
15+
16+
// MARK: - Errors
17+
let hasError: Observable<Bool>
18+
let errorMessage: Observable<String?>
19+
20+
// MARK: - Services
21+
private var eventsService: EventsServiceProtocol
22+
23+
24+
init () {
25+
hasError = Observable(false)
26+
errorMessage = Observable(nil)
27+
28+
self.allSchedules = Observable([])
29+
self.count = Observable(1)
30+
31+
// Dependency Injections
32+
eventsService = FossAsiaEventsService()
33+
34+
refreshDates()
35+
}
36+
37+
func refreshDates() {
38+
// Retrieve all dates
39+
eventsService.retrieveEventsInfo(nil, trackIds: nil) { (events, error) -> Void in
40+
var dates = Set<NSDate>()
41+
if let eventsArray = events {
42+
for event in eventsArray {
43+
let newDate = NSDate(year: event.startDateTime.year(), month: event.startDateTime.month(), day: event.startDateTime.day())
44+
dates.insert(newDate)
45+
}
46+
}
47+
let sortedDates = dates.sort({$0.compare($1) == .OrderedAscending})
48+
self.update(self.retrieveSchedule(sortedDates))
49+
}
50+
}
51+
52+
private func update(allSchedule: [ScheduleViewModel]) {
53+
self.allSchedules.value = allSchedule
54+
}
55+
56+
private func retrieveSchedule(dates: [NSDate]) -> [ScheduleViewModel] {
57+
let allSchedules = dates.map { date in
58+
return ScheduleViewModel(date)
59+
}
60+
return allSchedules
61+
}
62+
63+
}

FOSSAsia/EventsResultsViewController.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ class EventsResultsViewController: EventsBaseViewController {
2424

2525
// MARK: UITableViewDataSource
2626
extension EventsResultsViewController {
27+
2728
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
2829
return visibleEvents.count
2930
}
3031

3132
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
32-
let cell = tableView.dequeueReusableCellWithIdentifier(kEventCellReuseIdentifier, forIndexPath: indexPath) as! EventCell
33+
let cell = tableView.dequeueReusableCellWithIdentifier(EventsBaseViewController.kEventCellReuseIdentifier, forIndexPath: indexPath) as! EventCell
3334
let eventViewModel = visibleEvents[indexPath.row]
3435
cell.configure(withPresenter: eventViewModel)
3536

FOSSAsia/EventsServiceProtocol.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@ import Foundation
1111
typealias EventCompletionHandler = ([Event]?, Error?) -> Void
1212

1313
protocol EventsServiceProtocol {
14-
func retrieveEventsInfo(trackIds: [Int]?, completionHandler: EventCompletionHandler)
14+
func retrieveEventsInfo(date: NSDate?, trackIds: [Int]?, completionHandler: EventCompletionHandler)
1515
func toggleFavorite(sessionId: Int, completionHandler: EventsServiceCommitmentCompletionHandler)
1616
}

0 commit comments

Comments
 (0)