FacilitiesListViewController.swift 13.5 KB
Newer Older
1
2
3
4
5
//
//  LocationsListViewController.swift
//  WhatsOpen
//
//  Created by Zach Knox on 4/5/17.
Zach Knox's avatar
Zach Knox committed
6
//  Copyright © 2017 SRCT. Some rights reserved.
7
8
9
//

import UIKit
Zach Knox's avatar
Zach Knox committed
10
import DeckTransition
Zach Knox's avatar
Zach Knox committed
11
import RealmSwift
12

13
class FacilitiesListViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, UIViewControllerPreviewingDelegate {
14

Zach Knox's avatar
Zach Knox committed
15
	var facilitiesArray = List<Facility>()
Zac Wood's avatar
Zac Wood committed
16
17
18
19
20
    var filteredFacilities = List<Facility>()
    
    // passing in nil sets the controller to be this controller
    let searchController = UISearchController(searchResultsController: nil)

Zach Knox's avatar
Zach Knox committed
21
	var filters = Filters()
22
	
23
24
25
26
	override var preferredStatusBarStyle: UIStatusBarStyle {
		return .default
	}
	
27
	@IBOutlet var LeftButton: UIBarButtonItem!
Zach Knox's avatar
Zach Knox committed
28

29
30
31
32
	@IBAction func RightButton(_ sender: Any) {
	}
	@IBOutlet var RightButton: UIBarButtonItem!
	
33
34
	@IBOutlet var settingsButton: UIBarButtonItem!
	
35
	@IBOutlet var LocationsList: UICollectionView!
Zach Knox's avatar
Zach Knox committed
36

37
	@IBOutlet var LocationsListLayout: UICollectionViewFlowLayout!
Zach Knox's avatar
Zach Knox committed
38

39
	@IBOutlet var favoritesControl: UISegmentedControl!
Zach Knox's avatar
Zach Knox committed
40
41
	var showFavorites = false

42
	@IBOutlet var LastUpdatedLabel: UIBarButtonItem!
Zach Knox's avatar
Zach Knox committed
43

Zach Knox's avatar
Zach Knox committed
44
45
46
47
48
	@IBAction func favoritesControlChanges(_ sender: Any) {
		switch (self.favoritesControl.selectedSegmentIndex)
		{
		case 0:
			showFavorites = false
Zach Knox's avatar
Zach Knox committed
49
			filteredFacilities = facilitiesArray
Zach Knox's avatar
Zach Knox committed
50
51
		case 1:
			showFavorites = true
Zach Knox's avatar
Zach Knox committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
			filteredFacilities = List<Facility>()
			let defaults = UserDefaults.standard
			
			let favoriteStrings = defaults.array(forKey: "favorites") as! [String]?
			if(favoriteStrings == nil) {
				return
			}
			else {
				for facility in facilitiesArray {
					for str in favoriteStrings! {
						if(facility.facilityName == str) {
							filteredFacilities.append(facility)
							break;
						}
					}
				}
			}

Zach Knox's avatar
Zach Knox committed
70
71
72
73
74
		default:
			showFavorites = false
		}
		self.LocationsList.reloadData()
	}
Zach Knox's avatar
Zach Knox committed
75

Zach Knox's avatar
Zach Knox committed
76
	let refreshControl = UIRefreshControl()
Zach Knox's avatar
Zach Knox committed
77

78
	override func viewWillLayoutSubviews() {
Zach Knox's avatar
Zach Knox committed
79
80
81
82
83
84
		LocationsListLayout.itemSize.width = getCellWidth()
		LocationsListLayout.invalidateLayout()

	}
	
	func getCellWidth() -> CGFloat {
85
86
		let windowWidth = self.view.frame.size.width
		
Zach Knox's avatar
Zach Knox committed
87
88
		if(windowWidth < 640) {
			return windowWidth - 20
89
90
		}
		else if(windowWidth >= 640 && windowWidth < 1024) {
Zach Knox's avatar
Zach Knox committed
91
			return (windowWidth / 2) - 15
92
93
		}
		else if(windowWidth >= 1024) {
Zach Knox's avatar
Zach Knox committed
94
			return (windowWidth / 3) - 15
95
96
		}
		
Zach Knox's avatar
Zach Knox committed
97
		return 0
98
	}
Zach Knox's avatar
Zach Knox committed
99

100
101
102
	@IBAction func RefreshButton(_ sender: Any) {
		refresh(sender)
	}
Zach Knox's avatar
Zach Knox committed
103

104
105
106
107
	override func viewWillAppear(_ animated: Bool) {
		LastUpdatedLabel.isEnabled = false
	}
	
Zach Knox's avatar
Zach Knox committed
108
	@objc func tapRecognizer(_ sender: UITapGestureRecognizer) {
Zach Knox's avatar
Zach Knox committed
109
110
111
		
		let tapLocation = sender.location(in: LocationsList)
		let indexPath = LocationsList.indexPathForItem(at: tapLocation)
Zac Wood's avatar
Zac Wood committed
112
        
Zach Knox's avatar
Zach Knox committed
113
		if((indexPath) != nil) {
Zac Wood's avatar
Zac Wood committed
114
115
116
117
118
            let destination = self.storyboard?.instantiateViewController(withIdentifier: "detailView") as? FacilityDetailViewController
            let tapped = self.LocationsList.cellForItem(at: indexPath!) as! FacilityCollectionViewCell
            destination!.facility = tapped.facility
            self.presentDetailView(destination!)
            
Zach Knox's avatar
Zach Knox committed
119
120
121
122
123
124
125
		}
	}
	
	func presentDetailView(_ destination: FacilityDetailViewController) {
		if(self.view.traitCollection.horizontalSizeClass == .regular && self.view.traitCollection.verticalSizeClass == .regular) {
			//do a popover here for the iPad
			//iPads are cool right?
Zach Knox's avatar
Zach Knox committed
126
127
128
129
			destination.modalPresentationStyle = .popover
			let popoverController = destination.popoverPresentationController
			popoverController?.permittedArrowDirections = .any
			popoverController?.sourceView = destination.view
Zac Wood's avatar
Zac Wood committed
130
131
132
133
134
135
136
137
            
            // present the detail view over the search controller if we're searching
            if searchController.isActive {
                searchController.present(destination, animated: true, completion: nil)
            }
            else {
                present(destination, animated: true, completion: nil)
            }
Zach Knox's avatar
Zach Knox committed
138
139
140
141
142
		}
		else {
			let destDelegate = DeckTransitioningDelegate()
			destination.modalPresentationStyle = .custom
			destination.transitioningDelegate = destDelegate
Zac Wood's avatar
Zac Wood committed
143
144
145
146
147
148
149
150
            
            // present the detail view over the search controller if we're searching
            if searchController.isActive {
                searchController.present(destination, animated: true, completion: nil)
            }
            else {
                present(destination, animated: true, completion: nil)
            }
Zach Knox's avatar
Zach Knox committed
151
		}
Zac Wood's avatar
Zac Wood committed
152
153
        
        
Zach Knox's avatar
Zach Knox committed
154
	}
Zac Wood's avatar
Zac Wood committed
155
156
157
158
159
160
161
162
163
    
    func configureSearchController() {
        searchController.searchResultsUpdater = self
        searchController.obscuresBackgroundDuringPresentation = false
        
        // add it to the navigationItem
        navigationItem.searchController = searchController
        navigationItem.hidesSearchBarWhenScrolling = true
    }
Zach Knox's avatar
Zach Knox committed
164
165
	
	override func viewDidLoad() {
166
167
        super.viewDidLoad()
		
168
169
		self.definesPresentationContext = true
		
Zach Knox's avatar
Zach Knox committed
170
		if(traitCollection.forceTouchCapability == .available) {
171
172
			registerForPreviewing(with: self, sourceView: self.LocationsList!)
		}
Zach Knox's avatar
Zach Knox committed
173
174
        
        navigationItem.title = "What's Open"
175
		navigationController?.navigationBar.prefersLargeTitles = true
Zach Knox's avatar
Zach Knox committed
176
        
Zac Wood's avatar
Zac Wood committed
177
        configureSearchController()
Zach Knox's avatar
Zach Knox committed
178
		
179
		LocationsListLayout.invalidateLayout()
Zach Knox's avatar
Zach Knox committed
180
		
181
182
183
		settingsButton.accessibilityLabel = "Settings"
		LastUpdatedLabel.accessibilityHint = ""
		
184
		LocationsListLayout.sectionInset = UIEdgeInsetsMake(10, 10, 10, 10)
Zach Knox's avatar
Zach Knox committed
185

Zach Knox's avatar
Zach Knox committed
186
187
188
		refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
		LocationsList.addSubview(refreshControl)
		LocationsList.alwaysBounceVertical = true
Zach Knox's avatar
Zach Knox committed
189

190
		SRCTNetworkController.performDownload { (facilities) in
Zach Knox's avatar
Zach Knox committed
191
			self.facilitiesArray = List(facilities)
192
193
			DispatchQueue.main.async {
				self.LocationsList.reloadData()
194
195
				let date = Date()
				self.LastUpdatedLabel.title = "Updated: " + self.shortDateFormat(date)
196
197
			}
		}
198
199
		
	}
Zac Wood's avatar
Zac Wood committed
200
201
202
203
204
205
206
207
208
209
210
    
    func isSearchBarEmpty() -> Bool {
        return searchController.searchBar.text?.isEmpty ?? true
    }
    
    func isSearching() -> Bool {
        return searchController.isActive && !isSearchBarEmpty()
    }
    
    func filterFacilitiesForSearchText(_ searchText: String) {
        filteredFacilities = facilitiesArray.filter({(facility: Facility) -> Bool in
Zac Wood's avatar
Zac Wood committed
211
212
            let hasName = facility.facilityName.lowercased().contains(searchText.lowercased())
            let hasBuilding = facility.facilityLocation?.building.lowercased().contains(searchText.lowercased()) ?? false
213
214
			let hasCategory = facility.category?.categoryName.lowercased().contains(searchText.lowercased()) ?? false
            return hasName || hasBuilding || hasCategory
Zac Wood's avatar
Zac Wood committed
215
216
217
        })
        LocationsList.reloadData()
    }
Zach Knox's avatar
Zach Knox committed
218
	
Zach Knox's avatar
Zach Knox committed
219
	@objc func refresh(_ sender: Any) {
Zach Knox's avatar
Zach Knox committed
220
221
		refreshControl.beginRefreshing()
		LocationsList.reloadData()
222
223
		let date = Date()
		LastUpdatedLabel.title = "Updated: " + shortDateFormat(date)
Zach Knox's avatar
Zach Knox committed
224
225
		refreshControl.endRefreshing()
	}
Zac Wood's avatar
Zac Wood committed
226
    
227
228
229
230
231
232
233
234
235
	func shortDateFormat(_ date: Date) -> String {
		let dateFormatter = DateFormatter()
		dateFormatter.dateStyle = .short
		dateFormatter.timeStyle = .short

		// US English Locale (en_US)
		dateFormatter.locale = Locale(identifier: "en_US")
		return dateFormatter.string(from: date)
	}
Zach Knox's avatar
Zach Knox committed
236
	
237
238
239
240
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
Zach Knox's avatar
Zach Knox committed
241

242
	func numberOfSections(in collectionView: UICollectionView) -> Int {
Zach Knox's avatar
Zach Knox committed
243
		return 1
244
	}
Zach Knox's avatar
Zach Knox committed
245

246
	func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
Zach Knox's avatar
Zach Knox committed
247
        return isSearching() || showFavorites ? self.filteredFacilities.count : self.facilitiesArray.count
248
	}
Zach Knox's avatar
Zach Knox committed
249

250
	func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
251
		let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CollectionCell", for: indexPath) as! FacilityCollectionViewCell
Zach Knox's avatar
Zach Knox committed
252
253
254
255
256
257
258
		/*
		let windowRect = self.view.window!.frame
		let windowWidth = windowRect.size.width
		if(windowWidth <= 320) {
			cell.frame.size.width = 280
		}
		*/
Zach Knox's avatar
Zach Knox committed
259
        //Get tap of the cell
260
		cell.tapRecognizer.addTarget(self, action: #selector(FacilitiesListViewController.tapRecognizer(_:)))
Zach Knox's avatar
Zach Knox committed
261
        cell.gestureRecognizers = []
262
		cell.gestureRecognizers?.append(cell.tapRecognizer)
Zach Knox's avatar
Zach Knox committed
263
        
Zac Wood's avatar
Zac Wood committed
264
265
266
267
268
        
        let facility: Facility
        let dataArray: [Facility]
        
        // if something has been searched for, we want to use the filtered array as the data source
Zach Knox's avatar
Zach Knox committed
269
        if isSearching() || showFavorites {
Zac Wood's avatar
Zac Wood committed
270
271
272
273
274
275
276
            dataArray = placeOpenFacilitiesFirstInArray(filteredFacilities)
        } else {
            dataArray = placeOpenFacilitiesFirstInArray(facilitiesArray)
        }
        
		facility = dataArray[indexPath.row]
        
277
		cell.facility = facility
Zach Knox's avatar
Zach Knox committed
278
279
        
        //set labels
Zach Knox's avatar
Zach Knox committed
280
		cell.nameLabel.text = facility.facilityName
281
        cell.categoryLabel.text = facility.category?.categoryName.uppercased()
Zach Knox's avatar
Zach Knox committed
282

Zach Knox's avatar
Zach Knox committed
283
284
285
286
287
288
        cell.openClosedLabel.text = Utilities.openOrClosedUntil(facility)
        
        cell.timeDescriptionLabel.text = facility.facilityLocation?.building

        //change appearence based on open state
        let open = Utilities.isOpen(facility: facility)
289
		if(open == true) {
290
			//cell.openClosedLabel.text = "Open"
291
292
293
			cell.openClosedLabel.textColor = UIColor.black
			cell.openClosedLabel.backgroundColor = UIColor.white
			//cell.openClosedLabel.backgroundColor = UIColor(red:0.00, green:0.40, blue:0.20, alpha:1.0)
294
			cell.backgroundColor = UIColor(red:0.00, green:0.40, blue:0.20, alpha:1.0)
295
		} else {
296
			//cell.openClosedLabel.text = "Closed"
297
298
299
			cell.openClosedLabel.textColor = UIColor.white
			cell.openClosedLabel.backgroundColor = UIColor.black
			//cell.openClosedLabel.backgroundColor = UIColor.red
300
301
			cell.backgroundColor = UIColor.red

302
		}
Zach Knox's avatar
Zach Knox committed
303

Zach Knox's avatar
Zach Knox committed
304
        //Accessibility
305
        //TODO: FIX THIS
306
307
308
309
		cell.accessibilityLabel = cell.nameLabel.text! + ", Currently " + cell.openClosedLabel.text! + "." + cell.timeDescriptionLabel.text!
		cell.accessibilityHint = "Double Tap to view details"

		
310
311
312
		self.reloadInputViews()
		return cell
	}
Zach Knox's avatar
Zach Knox committed
313
314

	func getLocationArray(_ facilitiesArray: List<Facility>) -> [Facility] {
Zach Knox's avatar
Zach Knox committed
315
316
317
318
319
320
		if(!showFavorites) {
			return placeOpenFacilitiesFirstInArray(facilitiesArray)
		}
		else {
			return [] //TODO - INCOMPLETE
		}
Zach Knox's avatar
Zach Knox committed
321
322


Zach Knox's avatar
Zach Knox committed
323
	}
Zach Knox's avatar
Zach Knox committed
324

Zac Wood's avatar
Zac Wood committed
325
326
327
	// Returns an array which has the open locations listed first
	// Could be improved in the future because currently this means you're checking
	// open status twice per cell
Zach Knox's avatar
Zach Knox committed
328
	func placeOpenFacilitiesFirstInArray(_ facilitiesArray: List<Facility>) -> [Facility] {
329
330
		var open = [Facility]()
		var closed = [Facility]()
Zach Knox's avatar
Zach Knox committed
331

332
333
334
335
336
337
338
339
340
341
342
		for i in facilitiesArray {
			if(Utilities.isOpen(facility: i)) {
				open.append(i)
			}
			else {
				closed.append(i)
			}
		}
		// Test
		return open + closed
	}
Zach Knox's avatar
Zach Knox committed
343

344
345
346
	func countForOpenAndClosedFacilities(_ facilitiesArray: Array<Facility>) -> (open: Int, closed: Int) {
		var open = 0
		var closed = 0
Zach Knox's avatar
Zach Knox committed
347

348
349
350
351
352
353
354
355
		for i in facilitiesArray {
			if(Utilities.isOpen(facility: i)) {
				open += 1
			}
			else {
				closed += 1
			}
		}
Zach Knox's avatar
Zach Knox committed
356

357
358
		return (open, closed)
	}
Zach Knox's avatar
Zach Knox committed
359
360


361
362
    // MARK: - Navigation

Zach Knox's avatar
Zach Knox committed
363
    //In a storyboard-based application, you will often want to do a little preparation before navigation
Zac Wood's avatar
Zac Wood committed
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
//    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
//        // Get the new view controller using segue.destinationViewController.
//        if(segue.identifier == "toDetailView") {
//            let destination = segue.destination as! FacilityDetailViewController
//            let destDelegate = DeckTransitioningDelegate()
//            destination.transitioningDelegate = destDelegate
//            let tapped = sender as! FacilityCollectionViewCell //this is probably a bad idea just FYI future me
//            destination.facility = tapped.facility
//
//            // if we're in the search view, present on its controller
//            if searchController.isActive {
//                searchController.present(destination, animated: true, completion: nil)
//            } else {
//                present(destination, animated: true, completion: nil)
//            }
//        }
//        else if(segue.identifier == "toFilters") {
//            let destination = segue.destination as! UINavigationController
//            let filterView = destination.topViewController as! FiltersTableViewController
//            filterView.filters = self.filters
//        }
//
//        // Pass the selected object to the new view controller.
//    }
388
	
389
390
391
392
	// MARK: - Peek and Pop
	
	func previewingContext(_ previewingContext: UIViewControllerPreviewing, viewControllerForLocation location: CGPoint) -> UIViewController? {
		guard let indexPath = LocationsList?.indexPathForItem(at: location) else { return nil }
393
394
		let cell = LocationsList?.cellForItem(at: indexPath) as! FacilityCollectionViewCell
		guard let detailView = storyboard?.instantiateViewController(withIdentifier: "detailView") as? FacilityDetailViewController else { return nil }
395
396
		
		detailView.facility = cell.facility
Zach Knox's avatar
Zach Knox committed
397

398
399
400
401
		return detailView
	}
	
	func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController) {
Zach Knox's avatar
Zach Knox committed
402
403
404
405
406
		let destDelegate = DeckTransitioningDelegate()
		viewControllerToCommit.modalPresentationStyle = .custom
		viewControllerToCommit.transitioningDelegate = destDelegate
		//If one day 3D touch comes to the iPad, this is no longer good.
		present(viewControllerToCommit, animated: true, completion: nil)
407
408
	}
	
409
}
Zac Wood's avatar
Zac Wood committed
410
411
412
413
414
415
416
417
418

// by implementing UISearchResultsUpdating, we can use this controller for the search controller
extension FacilitiesListViewController: UISearchResultsUpdating {
    func updateSearchResults(for searchController: UISearchController) {
        let searchText = searchController.searchBar.text ?? ""
        filterFacilitiesForSearchText(searchText)
    }
}