-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMapViewController.swift
More file actions
275 lines (222 loc) · 11.6 KB
/
Copy pathMapViewController.swift
File metadata and controls
275 lines (222 loc) · 11.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
//
// MapViewController.swift
// Belashi-iOS
//
// Created by Noah on 8/4/16.
// Copyright © 2016 AppDelegates. All rights reserved.
//
import UIKit
import MapKit
import CoreLocation
import SwiftyJSON
class MapViewController : UIViewController {
@IBOutlet weak var mapView: MKMapView!
@IBOutlet weak var tableView: UITableView!
var annotations = [MKAnnotation]()
var currentLocation: CLLocation!
let nc = NotificationCenter.default
func processVenues( _ inboundVenueJson: JSON ){
guard let venueArray = inboundVenueJson.array else {
log.debug("No venues found!")
return
}
var existingLocations = [String]()
// val is one restaurant object
for venue in venueArray {
let address = venue["address"]
let name = venue["name"].stringValue
// Address components compiled into one human readable string
let location = String(format: "%@, %@, %@, %@", address["street"].stringValue, address["city"].stringValue, address["state"].stringValue, address["zip"].stringValue)
// There were some duplicates in the database so I'm filtering those out here. We don't really need this in the future though because that's more a database issue
if existingLocations.contains(location) {
continue
}else {
existingLocations.append(location)
}
let geocoder: CLGeocoder = CLGeocoder()
// Convert address into coordinates for visual map items
geocoder.geocodeAddressString(location,completionHandler: {(placemarks: [CLPlacemark]?, error: Error?) -> Void in
if ((placemarks?.count)! > 0) {
let topResult: CLPlacemark = (placemarks?[0])!
let placemark = MKPlacemark(placemark: topResult)
let pointAnnotation = MKPointAnnotation()
pointAnnotation.coordinate = placemark.coordinate
pointAnnotation.title = name
pointAnnotation.subtitle = location
self.mapView.addAnnotation(pointAnnotation)
}
} as! CLGeocodeCompletionHandler) // changed NSError in 54 to error to avoid app crach
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.delegate = self
self.tableView.dataSource = self
let recenterBarButton = UIBarButtonItem(title: "Recenter", style: .plain, target: self, action: #selector(recenter))
self.navigationItem.rightBarButtonItem = recenterBarButton
self.mapView.delegate = self
nc.addObserver(self, selector: #selector(networkChanged), name: NSNotification.Name(rawValue: ASNotification.networkChanged.rawValue), object: nil)
Asahi.sharedInstance.getVenues()
.then { response -> Void in
log.debug("Got venues!")
self.processVenues(response)
}
.catch{ err -> Void in
log.error("Error getting venues")
print(err)
}
}
func recenter() {
// Zoom in on current location
// 0.3 is some number. The smaller it is, the smaller the frame (huh, makes sense right?)
self.mapView.setRegion(MKCoordinateRegionMake(self.currentLocation.coordinate, MKCoordinateSpanMake(0.3, 0.3)), animated: true)
}
// reload venues when network changes
func networkChanged() {
Asahi.sharedInstance.getVenues()
.then { response -> Void in
log.debug("Got venues!")
self.processVenues(response)
}
.catch{ err -> Void in
log.error("Error getting venues")
print(err)
}
}
}
extension MapViewController : UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.annotations.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "OGMapItemCell") as UITableViewCell?
if cell == nil {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "OGMapItemCell")
cell?.backgroundColor = UIColor(white: 51/255, alpha: 1.0)
cell?.textLabel?.textColor = UIColor.white
cell?.detailTextLabel?.textColor = UIColor( white: 1.0, alpha: 0.7)
cell?.textLabel?.font = UIFont(name: Style.regularFont, size: 15.0)
cell?.detailTextLabel?.font = UIFont(name: Style.lightFont, size: 12.0)
}
let annotation = self.annotations[indexPath.row]
cell!.textLabel!.text = annotation.title!
if cell!.textLabel!.text! == "Current Location" {
cell!.textLabel!.font = UIFont(name: Style.regularFont, size: 15.0)
cell!.detailTextLabel!.font = UIFont(name: Style.lightFont, size: 12.0)
cell!.detailTextLabel!.text = ""
}else {
cell!.textLabel!.font = UIFont(name: Style.regularFont, size: 15.0)
cell!.detailTextLabel!.font = UIFont(name: Style.lightFont, size: 12.0)
cell!.detailTextLabel!.text = annotation.subtitle!
}
return cell!
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// On select or deselect of row, select or deselect that pin (aka annotation)
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let annotation = self.annotations[indexPath.row]
if self.mapView.view(for: annotation)!.isSelected {
self.mapView.deselectAnnotation(annotation, animated: true)
tableView.deselectRow(at: indexPath, animated: true)
}else {
self.mapView.selectAnnotation(annotation, animated: true)
}
}
}
extension MapViewController : MKMapViewDelegate {
// When the map view updates the current location, update the class variable.
// But only recenter it upon getting this location the very first time, hence,
// currentLocation would be nil to start off. If the user moves around, we don't
// want to just zoom them back in or they can't look around
func mapView(_ mapView: MKMapView, didUpdate userLocation: MKUserLocation) {
let shouldCenter = self.currentLocation == nil
self.currentLocation = userLocation.location!
if shouldCenter {
recenter()
}
}
// Requested when creating the pin (aka annotation) view
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
if annotation.title! != "Current Location" {
let pinAnnotationView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "OurglasserPin")
pinAnnotationView.animatesDrop = true
// This is the green color RGB for Ourglass logo
pinAnnotationView.pinTintColor = UIColor(red: (57.0/255.0), green: (172.0/255.0), blue: (72.0/255.0), alpha: 1.0)
pinAnnotationView.canShowCallout = true
// Make clickable
let button = UIButton(type: .detailDisclosure)
pinAnnotationView.rightCalloutAccessoryView = button
return pinAnnotationView
}else {
return nil
}
}
// Link deselection of pin (aka annotation) to table view
func mapView(_ mapView: MKMapView, didDeselect view: MKAnnotationView) {
let annotation = view.annotation!
let index = self.annotations.index(where: {
$0.title! == annotation.title!
})!
self.tableView.deselectRow(at: IndexPath(row: index, section: 0), animated: true)
}
// Link selection of pin (aka annotation) to table view
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
let annotation = view.annotation!
let index = self.annotations.index(where: {
$0.title! == annotation.title!
})!
self.tableView.selectRow(at: IndexPath(row: index, section: 0), animated: true, scrollPosition: .middle)
}
// If the region changes, we want to remove what's in the table view
// For one, if you try to open an annotation view that isn't on screen,
// it crashes. But also it helps the user to see what's around them, and
// if they zoom out, they can see more. You can make this zoom out to fit
// if the annotation isn't in view if you want, but I thought this worked nicely.
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
// Order annotations by distance from user
let visibleAnnotations = (Array(self.mapView.annotations(in: self.mapView.visibleMapRect)) as NSArray) as! [MKAnnotation]
self.annotations = [MKAnnotation]()
self.annotations = visibleAnnotations.sorted(by: { (ann1: MKAnnotation, ann2: MKAnnotation) -> Bool in
// Always order Current Location first
// Also, if currentLocation isn't set yet, it will crash trying to determine distance, obviously.
// This method is called once when location is found beacuse map recents and the region changes, so
// the list will reload anyways.
if ann1.title! == "Current Location" || self.currentLocation == nil {
return true
}
let coordLoc1 = CLLocation(latitude: ann1.coordinate.latitude, longitude: ann1.coordinate.longitude)
let dist1 = self.currentLocation.distance(from: coordLoc1)
let coordLoc2 = CLLocation(latitude: ann2.coordinate.latitude, longitude: ann2.coordinate.longitude)
let dist2 = self.currentLocation.distance(from: coordLoc2)
return dist1 < dist2
})
self.tableView.reloadData()
}
// Order annotations when they are first added to map
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
// Order annotations by distance from user
let visibleAnnotations = (Array(self.mapView.annotations(in: self.mapView.visibleMapRect)) as NSArray) as! [MKAnnotation]
self.annotations = [MKAnnotation]()
self.annotations = visibleAnnotations.sorted(by: { (ann1: MKAnnotation, ann2: MKAnnotation) -> Bool in
if ann1.title! == "Current Location" || self.currentLocation == nil {
return true
}
let coordLoc1 = CLLocation(latitude: ann1.coordinate.latitude, longitude: ann1.coordinate.longitude)
let dist1 = self.currentLocation.distance(from: coordLoc1)
let coordLoc2 = CLLocation(latitude: ann2.coordinate.latitude, longitude: ann2.coordinate.longitude)
let dist2 = self.currentLocation.distance(from: coordLoc2)
return dist1 < dist2
})
self.tableView.reloadData()
}
// Open in apple maps! My favorite...
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
// Make restaurant map item
let placemark = MKPlacemark(coordinate: view.annotation!.coordinate, addressDictionary: nil)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = view.annotation!.title!
mapItem.openInMaps(launchOptions: [MKLaunchOptionsDirectionsModeKey:MKLaunchOptionsDirectionsModeDriving])
}
}