Im new in coding. is there a way to show multiple drivers around the user.
.
I've already figured out how to show a list of drivers near the user thru database, now I want the map to show the drivers near the user.
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
import MapKit
class EmployeeTableViewController: UITableViewController, CLLocationManagerDelegate {
#IBOutlet weak var jobsAvailableMap: MKMapView!
var jobRequests : [DataSnapshot] = []
var locationManager = CLLocationManager()
var employeeLocation = CLLocationCoordinate2D()
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestWhenInUseAuthorization()
locationManager.startUpdatingLocation()
Database.database().reference().child("JobRequests").observe(.childAdded) { (snapshot) in
if let jobRequestDictionary = snapshot.value as? [String:AnyObject] {
if let employeeLat = jobRequestDictionary["employeeLat"] as? Double {
} else {
self.jobRequests.append(snapshot)
self.tableView.reloadData()
}
}
}
Timer.scheduledTimer(withTimeInterval: 3, repeats: true) { (timer) in
self.tableView.reloadData()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let coord = manager.location?.coordinate {
employeeLocation = coord
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return jobRequests.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "jobRequestCell", for: indexPath)
let snapshot = jobRequests[indexPath.row]
if let jobRequestDictionary = snapshot.value as? [String:AnyObject] {
if let email = jobRequestDictionary["email"] as? String {
if let lat = jobRequestDictionary["lat"] as? Double {
if let lon = jobRequestDictionary["lon"] as? Double {
let employeeCLLocation = CLLocation(latitude: employeeLocation.latitude, longitude: employeeLocation.longitude)
let employerCLLocation = CLLocation(latitude: lat, longitude: lon)
let distance = employeeCLLocation.distance(from: employerCLLocation) / 1000
let roundedDistance = round(distance * 100) / 100
cell.textLabel?.text = "\(email) - \(roundedDistance)km away"
}
}
}
}
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let snapshot = jobRequests[indexPath.row]
performSegue(withIdentifier: "acceptSegue", sender: snapshot)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let acceptVC = segue.destination as? AcceptJobViewController {
if let snapshot = sender as? DataSnapshot {
if let jobRequestDictionary = snapshot.value as? [String:AnyObject] {
if let email = jobRequestDictionary["email"] as? String {
if let lat = jobRequestDictionary["lat"] as? Double {
if let lon = jobRequestDictionary["lon"] as? Double {
acceptVC.requestEmail = email
let location = CLLocationCoordinate2D(latitude: lat, longitude: lon)
acceptVC.requestLocation = location
acceptVC.employeeLocation = employeeLocation
}
}
}
}
}
}
}
#IBAction func logoutTapped(_ sender: Any) {
try? Auth.auth().signOut()
navigationController?.dismiss(animated: true, completion: nil)
}
}
I'm trying to look for tutorials online but most are not connected to Firebase Database.
You can use addAnnotation method.
e.g. (no guarantee you can build the following code)
func addDriverAnnotation(snapshot: DataSnapshot){
if let jobRequestDictionary = snapshot.value as? [String:AnyObject] {
if let email = jobRequestDictionary["email"] as? String {
if let lat = jobRequestDictionary["lat"] as? Double {
if let lon = jobRequestDictionary["lon"] as? Double {
let annotation = MKAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(lat, lon)
annotation.title = "title"
annotation.subtitle = "subtitle"
self.jobsAvailableMap.addAnnotation(annotation)
}
}
}
}
}
You need to call this method in Database.database()... method.
Database.database().reference().child("JobRequests").observe(.childAdded) { (snapshot) in
if let jobRequestDictionary = snapshot.value as? [String:AnyObject] {
if let employeeLat = jobRequestDictionary["employeeLat"] as? Double {
} else {
self.jobRequests.append(snapshot)
self.tableView.reloadData()
self.addDriverAnnotation(snapshot: snapshot)
}
}
}
Related
I want to use the latitude and logitude values in the structure array to represent polylines.
You can use the list[1].latitude value from json in the view, but if you try to use the list[1].latitude value from the override func viewDidLoad () above, you get an error.
How can I use this value as a polyline's latitude and longitude value?
Swift Source
import UIKit
import GoogleMaps
class TableController: UIViewController, GMSMapViewDelegate , UITableViewDataSource, UITableViewDelegate{
#IBOutlet weak var viewMap: GMSMapView!
#IBOutlet weak var listTableView: UITableView!
//JSON담을 구조
var list:[MyStruct] = [MyStruct]()
struct MyStruct
{
var index = ""
var ip = ""
var latitude = ""
var longitude = ""
var zonename = ""
init(_ index:String, _ ip:String, _ latitude:String, _ longitude:String, _ zonename:String)
{
self.index = index
self.ip = ip
self.latitude = latitude
self.longitude = longitude
self.zonename = zonename
}
}
override func viewDidLoad() {
super.viewDidLoad()
// set initial location
let camera: GMSCameraPosition = GMSCameraPosition.camera(withLatitude: 37.209037, longitude: 126.976370, zoom: 16.0)
viewMap.camera = camera
let count: Int = 0
//폴리라인
let path = GMSMutablePath()
path.add(CLLocationCoordinate2DMake(37.209037, 126.976370))
path.add(CLLocationCoordinate2DMake(37.211643, 126.972444))
path.add(CLLocationCoordinate2DMake(37.211457, 126.972147))
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 6.0
polyline.map = viewMap
listTableView.dataSource = self
listTableView.delegate = self
get_data("http://192.168.0.11/fence.php")
}
func get_data(_ link:String)
{
let url:URL = URL(string: link)!
let session = URLSession.shared
let request = URLRequest(url: url)
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
self.extract_data(data)
})
task.resume()
}
func extract_data(_ data:Data?)
{
let json:Any?
if(data == nil)
{
return
}
do{
json = try JSONSerialization.jsonObject(with: data!, options: [])
}
catch
{
return
}
guard let data_array = json as? NSArray else
{
return
}
for i in 0 ..< data_array.count
{
if let data_object = data_array[i] as? NSDictionary
{
if let data_index = data_object["index"] as? String,
let data_ip = data_object["Ip"] as? String,
let data_latitude = data_object["Latitude"] as? String,
let data_longitude = data_object["Longitude"] as? String,
let data_Zonename = data_object["ZoneName"] as? String
{
list.append(MyStruct(data_index, data_ip, data_latitude, data_longitude, data_Zonename))
}
}
}
refresh_now()
}
func refresh_now()
{
DispatchQueue.main.async(
execute:
{
self.listTableView.reloadData()
})
}
public func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return list.count
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = list[indexPath.row].zonename + " : " + list[indexPath.row].latitude
return cell
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I am doing a projects by following this tutorial enter link description here in swift3 Xcode 8.2.1.
I want to get 10 Nearest Restaurants from my current location.
But Still I can't get any data as map pins or tableview.
Please help me I am new to Swift.
Please find whole coding here including pod file.
Pod File
'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!
target "FSResturant" do
pod 'QuadratTouch',
:git => 'https://github.com/Constantine-Fry/das-quadrat', :branch => 'fry-swift30'
pod 'RealmSwift'
end
ResturantAPI.swift
import Foundation
import QuadratTouch
import MapKit
import Realm
import RealmSwift
//Create Venues Struct
struct API
{
struct notifications
{
static let venuesUpdated = "venues_updated"
}
}
class ResturantAPI
{
static let sharedInstance = ResturantAPI()
var session:Session?
init()
{
// Initialize the Foursquare client API Keys
let client = Client(clientID: "XXXX", clientSecret: "XXXX", redirectURL: "")
let configuration = Configuration(client:client)
Session.setupSharedSessionWithConfiguration(configuration)
self.session = Session.sharedSession()
}
//getting venue data from FOURSQUARE
func getResturantsWithLocation(location:CLLocation)
{
//function body
if let session = self.session
{
//this category Id will receive nearby halal resturants in dubai
var parameters = location.parameters()
parameters += [Parameter.categoryId: "52e81612bcbc57f1066b79ff"]
parameters += [Parameter.radius: "2000"]
parameters += [Parameter.limit: "10"]
// Start a "search", i.e. an async call to Foursquare that should return venue data
let searchTask = session.venues.search(parameters)
{
(result) -> Void in
if let response = result.response
{
if let venues = response["venues"] as? [[String: AnyObject]]
{
autoreleasepool
{
let realm = try! Realm()
realm.beginWrite()
for venue:[String: AnyObject] in venues
{
let venueObject:Venue = Venue()
if let id = venue["id"] as? String
{
venueObject.id = id
}
if let name = venue["name"] as? String
{
venueObject.name = name
}
if let location = venue["location"] as? [String: AnyObject]
{
if let longitude = location["lng"] as? Float
{
venueObject.longitude = longitude
}
if let latitude = location["lat"] as? Float
{
venueObject.latitude = latitude
}
if let formattedAddress = location["formattedAddress"] as? [String]
{
venueObject.address = formattedAddress.joined(separator: " ")
}
}
realm.add(venueObject, update: true)
}
do {
try realm.commitWrite()
print("Committing write...")
}
catch (let e)
{
print("Y U NO REALM ? \(e)")
}
}
NotificationCenter.default.post(name: Foundation.Notification.Name(rawValue: API.notifications.venuesUpdated), object:nil, userInfo:nil)
}
}
}
searchTask.start()
}
}
}
extension CLLocation
{
func parameters() -> Parameters
{
let ll = "\(self.coordinate.latitude),\(self.coordinate.longitude)"
// let near = "Dubai"
let llAcc = "\(self.horizontalAccuracy)"
// let llAcc = "10000.0"
let alt = "\(self.altitude)"
// let alt = "0"
let altAcc = "\(self.verticalAccuracy)"
// let altAcc = "10000.0"
// let query = "resturants"
let parameters = [
Parameter.ll:ll,
// Parameter.near:near,
Parameter.llAcc:llAcc,
Parameter.alt:alt,
Parameter.altAcc:altAcc,
// Parameter.query:query
]
return parameters
}
}
ViewController.swift
import UIKit
import MapKit
import RealmSwift
import Realm
class ViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, UITableViewDataSource, UITableViewDelegate {
//Initialize the Map View
#IBOutlet var mapView:MKMapView?
//To get the user location
var locationManager:CLLocationManager?
//Span view in meters default
let distanceSpan:Double = 500
//show user's location on the map
var lastLocation:CLLocation?
// Stores venues from Realm, as a non-lazy list
var venues:[Venue]?
//Initializing the table view
#IBOutlet var tableView1:UITableView?
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.onVenuesUpdated(_:)), name: NSNotification.Name(rawValue: API.notifications.venuesUpdated), object: nil);
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
//Initializing mapview delegate to display
if let mapView = self.mapView
{
mapView.delegate = self
}
//Initializing UITableview delegate to display
if let tableView1 = self.tableView1
{
tableView1.delegate = self
tableView1.dataSource = self
}
}
override func viewDidAppear(_ animated: Bool)
{
if locationManager == nil
{
locationManager = CLLocationManager()
locationManager!.delegate = self
locationManager!.desiredAccuracy = kCLLocationAccuracyBestForNavigation
locationManager!.requestAlwaysAuthorization()
// Don't send location updates with a distance smaller than 50 meters between them
locationManager!.distanceFilter = 50
locationManager!.startUpdatingLocation()
}
}
// UITable View Delegates
func tableView(_ tableView1: UITableView, numberOfRowsInSection section: Int) -> Int
{
return venues?.count ?? 0
}
func numberOfSections(in tableView1: UITableView) -> Int {
return 1
}
func tableView(_ tableView1: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView1.dequeueReusableCell(withIdentifier: "cellIdentifier");
if cell == nil
{
cell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: "cellIdentifier")
}
if let venue = venues?[indexPath.row]
{
cell!.textLabel?.text = venue.name
cell!.detailTextLabel?.text = venue.address
}
return cell!
}
func tableView(_ tableView1: UITableView, didSelectRowAt indexPath: IndexPath) {
if let venue = venues?[indexPath.row]
{
let region = MKCoordinateRegionMakeWithDistance(CLLocationCoordinate2D(latitude: Double(venue.latitude), longitude: Double(venue.longitude)), distanceSpan, distanceSpan)
mapView?.setRegion(region, animated: true)
}
}
func refreshVenues(_ location: CLLocation?, getDataFromFoursquare:Bool = false)
{
if location != nil
{
lastLocation = location
}
if let location = lastLocation
{
if getDataFromFoursquare == true
{
ResturantAPI.sharedInstance.getResturantsWithLocation(location: location)
}
// Convenience method to calculate the top-left and bottom-right GPS coordinates based on region (defined with distanceSpan)
let (start, stop) = calculateCoordinatesWithRegion(location);
// Set up a predicate that ensures the fetched venues are within the region
let predicate = NSPredicate(format: "latitude < %f AND latitude > %f AND longitude > %f AND longitude < %f", start.latitude, stop.latitude, start.longitude, stop.longitude);
let realm = try! Realm()
venues = realm.objects(Venue.self).filter(predicate).sorted {
location.distance(from: $0.coordinate) < location.distance(from: $1.coordinate);
};
for venue in venues!
{
let annotation = ResturantAnnotation(title: venue.name, subtitle: venue.address, coordinate: CLLocationCoordinate2D(latitude: Double(venue.latitude), longitude: Double(venue.longitude)));
mapView?.addAnnotation(annotation);
}
// RELOAD ALL THE DATAS !!!
tableView1?.reloadData()
}
}
//Delegate Method of CLLocation Manager
func locationManager(manager: CLLocationManager, didUpdateToLocation newLocation: CLLocation, fromLocation oldLocation: CLLocation)
{
if let mapView = self.mapView
{
let region = MKCoordinateRegionMakeWithDistance(newLocation.coordinate, distanceSpan, distanceSpan)
mapView.setRegion(region, animated: true)
refreshVenues(newLocation, getDataFromFoursquare: true)
}
}
//Adds Annotations in the Map View
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?
{
if annotation.isKind(of: MKUserLocation.self)
{
return nil
}
var view = mapView.dequeueReusableAnnotationView(withIdentifier: "annotationIdentifier");
if view == nil
{
view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: "annotationIdentifier")
}
view?.canShowCallout = true
return view
}
func onVenuesUpdated(_ notification:Foundation.Notification)
{
// When new data from Foursquare comes in, reload from local Realm
refreshVenues(nil);
}
func calculateCoordinatesWithRegion(_ location:CLLocation) -> (CLLocationCoordinate2D, CLLocationCoordinate2D)
{
let region = MKCoordinateRegionMakeWithDistance(location.coordinate, distanceSpan, distanceSpan);
var start:CLLocationCoordinate2D = CLLocationCoordinate2D();
var stop:CLLocationCoordinate2D = CLLocationCoordinate2D();
start.latitude = region.center.latitude + (region.span.latitudeDelta / 2.0);
start.longitude = region.center.longitude - (region.span.longitudeDelta / 2.0);
stop.latitude = region.center.latitude - (region.span.latitudeDelta / 2.0);
stop.longitude = region.center.longitude + (region.span.longitudeDelta / 2.0);
return (start, stop);
}
Venue.swift
import Foundation
import RealmSwift
import MapKit
class Venue: Object
{
dynamic var id:String = ""
dynamic var name:String = ""
dynamic var latitude:Float = 0
dynamic var longitude:Float = 0
dynamic var address:String = ""
var coordinate:CLLocation {
return CLLocation(latitude: Double(latitude), longitude: Double(longitude));
}
//The the primary key to Realm
override static func primaryKey() -> String?
{
return "id";
}
}
ResturantAnnotation.swift
import Foundation
import MapKit
class ResturantAnnotation: NSObject, MKAnnotation
{
let title:String?
let subtitle:String?
let coordinate: CLLocationCoordinate2D
init(title:String?, subtitle:String?, coordinate:CLLocationCoordinate2D)
{
self.title=title
self.subtitle=subtitle
self.coordinate=coordinate
super.init();
}
}
Can Anybody figure out what is the mistake here, that not showing responses in the map and tableview.
Thanks.
I try to sort my tableView by distance form the current location. I search on the website but is it very difficult for me to adapte on my projet what I find
Can you help me please?
Here is my first files for data "Cables.swift":
import Foundation
import MapKit
class Person {
var identifier:Int
var name:String
var country:String
var email:String
var website:String
var facebook:String
var adress:String
var phone:String
var latitude:Double
var longitude:Double
var lac:Int
var poulie1:Int
var rotation1:String
var module1:Int
var poulie2:Int
var rotation2:String
var module2:Int
var distance:Double
var smallPhotoUrl:URL! {
return URL(string: "http://wakefinder.16mb.com/full/\(self.identifier).jpg")
}
var largePhotoUrl:URL! {
return URL(string: "http://wakefinder.16mb.com/full/\(self.identifier).jpg")
}
//var coordinate: CLLocationCoordinate2D
init?(fromData personData:[String:AnyObject]) {
guard let identifier = personData["id"] as? Int,
let name = personData["name"] as? String,
let country = personData["country"] as? String,
let email = personData["email"] as? String,
let website = personData["website"] as? String,
let facebook = personData["facebook"] as? String,
let adress = personData["adress"] as? String,
let phone = personData["phone"] as? String,
let latitude = personData["lat"] as? Double,
let longitude = personData["lng"] as? Double,
let lac = personData["lac"] as? Int,
let poulie1 = personData["poulie1"] as? Int,
let rotation1 = personData["rotation1"] as? String,
let module1 = personData["module1"] as? Int,
let poulie2 = personData["poulie2"] as? Int,
let rotation2 = personData["rotation2"] as? String,
let module2 = personData["module2"] as? Int
else {
return nil
}
self.identifier = identifier
self.name = name
self.country = country
self.email = email
self.website = website
self.facebook = facebook
self.adress = adress
self.phone = phone
self.latitude = latitude
self.longitude = longitude
self.lac = lac
self.poulie1 = poulie1
self.rotation1 = rotation1
self.module1 = module1
self.poulie2 = poulie2
self.rotation2 = rotation2
self.module2 = module2
}
// Function to calculate the distance from given location.
func calculateDistance(fromLocation: CLLocation?) {
let location = CLLocation(latitude: self.latitude, longitude: self.longitude)
distance = location.distance(from: fromLocation!)
}
}
and the file for my TableView:
import UIKit
import Alamofire
import CoreLocation
class cableViewController: UITableViewController, UISearchResultsUpdating {
var _personList:[Person] = []
var _personFiltered:[Person] = []
let searchController = UISearchController(searchResultsController: nil)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
Alamofire.request("http://wakefinder.16mb.com/users.json")
.validate()
.responseJSON { (response) in
if response.result.isSuccess {
let rawPersonList = response.result.value as! [[String:AnyObject]]
for personData in rawPersonList {
if let personObject = Person(fromData: personData) {
self._personList.append(personObject)
}
}
self.tableView.reloadData()
} else {
print(response.result.error as Any)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if searchController.isActive && searchController.searchBar.text != "" {
return _personFiltered.count
}
return _personList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell:cablesTableViewCell! = tableView.dequeueReusableCell(withIdentifier: "cable_cell") as! cablesTableViewCell
if searchController.isActive && searchController.searchBar.text != "" {
let person:Person = _personFiltered[indexPath.row]
cell.display(person: person)
//_personList[indexPath.row] = _personFiltered[indexPath.row]
} else {
let person:Person = _personList[indexPath.row]
cell.display(person: person)
}
return cell
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showDetail" {
if let cell = sender as? UITableViewCell {
if let indexPath = self.tableView.indexPath(for: cell) {
let selectedPerson: Person
if searchController.isActive && searchController.searchBar.text != "" {
selectedPerson = _personFiltered[indexPath.row]
} else {
selectedPerson = _personList[indexPath.row]
}
let personViewController:fichesViewController = segue.destination as! fichesViewController
personViewController._person = selectedPerson
}
}
}
}
func updateSearchResults(for searchController: UISearchController) {
filterContent(searchText: self.searchController.searchBar.text!)
}
func filterContent(searchText:String) {
_personFiltered = _personList.filter { user in
let username = user.name
return(username.lowercased().contains(searchText.lowercased()))
}
self.tableView.reloadData()
}
}
So when I select an item from the tableview, it segues into another viewcontroller and its passes data through that, I have it set that I segue back into the parent view controller. however it loses that data from the tableview, I tried to re-run the method that loads it but that doesn't seem to work, ideas as to what I am missing? I try to re-run methods that I think will do the job, but I'm not sure.
var locationManager = CLLocationManager()
var point = PFGeoPoint(latitude: 0.0, longitude: 0.0)
var vList = [Details]()
#IBOutlet var vListTableView: UITableView!
#IBOutlet var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.requestAlwaysAuthorization()
locationManager.startUpdatingLocation()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell{
let cell = self.vListTableView.dequeueReusableCell(withIdentifier: "venueDetailCell",for: indexPath) as! DetailsTableViewCell
cell.Distance.text = String(vList[indexPath.row].distance)
cell.title.text = vList[indexPath.row].name
return cell
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return vList.count
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
downloadDataFromDB(location: point)
self.vListTableView.reloadData()
locationManager.startUpdatingLocation()
}
override func prepare(for segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "vDetailToCommentList"){
var selectedRowIndex = self.vListTableView.indexPathForSelectedRow
let moveViewCont:CommentsViewController = segue.destination as! CommentsViewController
moveViewCont.Test = "Data pass successful"
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]){
let userLocation: CLLocation = locations[0]
let latitude: CLLocationDegrees = userLocation.coordinate.latitude
let longitude: CLLocationDegrees = userLocation.coordinate.longitude
let latDelta: CLLocationDegrees = 0.05
let lonDelta: CLLocationDegrees = 0.05
let span: MKCoordinateSpan=MKCoordinateSpan(latitudeDelta: latDelta, longitudeDelta: lonDelta)
let location: CLLocationCoordinate2D = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
let region: MKCoordinateRegion = MKCoordinateRegion(center: location, span: span)
map.setRegion(region, animated: true)
map.showsUserLocation = true
point = PFGeoPoint(latitude:latitude, longitude:longitude)
updateUserLocationinDB(location: point)
downloadDataFromDB(location: point)
}
func updateUserLocationinDB(location: PFGeoPoint){
let uAct = PFQuery(className:"uAct")
uAct.whereKey("userId", equalTo:(PFUser.current()?.username)!)
uAct.findObjectsInBackground(block: {(objects, error) in
if error != nil{
print(error)
}else if let users = objects {
if objects?.count>0{
for objects in users{
objects["UserLocation"] = location
objects.saveInBackground()
}
}
}
})
}
func downloadDataFromDB(location: PFGeoPoint){
locationManager.stopUpdatingLocation()
vList.removeAll()
let qHotSpots = PFQuery(className: "HotSpots")
qHotSpots.whereKey("venueLocation", nearGeoPoint: location, withinMiles: 10)
do{
let qReply = try qHotSpots.findObjects()
if qReply.count>10{
for object in qReply{
let curDetails:Details = Details()
let name:String = object.object(forKey:"venue")! as! String
let id:String = object.objectId!
let distance:Double = 0.0
curDetails.name = name
print("vList size",self.vList.count)
}
}
else if qReply.count == 0{
//TODO =: Download from API
}
}
catch{
print(error)
}
}
class Details {
var id:String = ""
var name:String = ""
var distance:Double = 0.0
func Details(iD: String,nam: String,dist: Double){
self.id = iD
self.name = nam
self.distance = dist
}
Maybe I am missing something?
Here's what I have tried
After thinking about when I should update the vListTableView: UITableView!, it makes sense that it should be updated immediately after populating vList. So it makes enough sense to me that the self.vListTableView.reloadData() should be called at the end of downloadDataFromDB as such:
func downloadDataFromDB(location: PFGeoPoint){
locationManager.stopUpdatingLocation()
vList.removeAll()
let qHotSpots = PFQuery(className: "HotSpots")
qHotSpots.whereKey("venueLocation", nearGeoPoint: location, withinMiles: 10)
do{
let qReply = try qHotSpots.findObjects()
if qReply.count>10{
for object in qReply{
let curDetails:Details = Details()
let name:String = object.object(forKey:"venue")! as! String
let id:String = object.objectId!
let distance:Double = 0.0
curDetails.name = name
//print("vList size",self.vList.count)
}
//THE FIX WAS HERE
self.venueListTableView.reloadData()
}
else if qReply.count == 0{
//TODO =: Download from API
}
}
catch{
print(error)
}
This way, reloaddata is always called at the end of downloadDataFromDB and it is never needed anywhere else.
Like clockwork.
HERE is the latest code. I've tried moving the tableView.reloadData() all over the place - anyone have any ideas or suggestions? I am still staring at an empty table that says "no results" when i make my API call and get exactly what I need in return.
I have also reset the constraints all over my storyboard - I feel like I am missing something very simple here
import UIKit
import Alamofire
import MapKit
//import CoreLocation
class ToolTableViewController: UITableViewController, CLLocationManagerDelegate {
// #IBOutlet weak var tableview: UITableView!
var jsonArray:NSMutableArray?
var tools = [Tool]()
#IBOutlet weak var toolListSearchBar: UISearchBar!
let locationManager = CLLocationManager()
var currentLat: CLLocationDegrees = 0.0
var currentLong: CLLocationDegrees = 0.0
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let locValue:CLLocationCoordinate2D = manager.location!.coordinate
// print("locations = \(locValue.latitude) \(locValue.longitude)")
let location = locations.last! as CLLocation
currentLat = location.coordinate.latitude
currentLong = location.coordinate.longitude
}
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.dataSource = self
self.tableView.delegate = self
dispatch_async(dispatch_get_main_queue(), { self.tableView.reloadData() })
self.tableView.registerClass(ToolTableViewCell.self, forCellReuseIdentifier: "ToolTableViewCell")
self.locationManager.delegate = self
self.locationManager.requestAlwaysAuthorization()
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
locationManager.startUpdatingLocation()
}
else{
print("Location service disabled");
}
// self.tableView.reloadData()
// Load the sample data.
}
func refresh(sender: AnyObject) {
// Reload the data
self.tableView.reloadData()
}
func searchBarSearchButtonClicked(searchbar: UISearchBar)
{
searchbar.resignFirstResponder()
tools = []
let defaults = NSUserDefaults.standardUserDefaults()
let userid: Int = defaults.objectForKey("toolBeltUserID") as! Int
let searchTerm = String(toolListSearchBar.text!)
print(searchTerm)
Alamofire.request(.GET, "http://localhost:3000/tools/search", parameters: ["keyword": searchTerm, "latitude": currentLat, "longitude": currentLong,
"user": userid]) .responseJSON {response in
if let JSON = response.result.value {
print("\(JSON)")
for i in 0 ..< JSON.count {
let owner = JSON[i].objectForKey("owner")
let tool = JSON[i].objectForKey("tool")
let title = tool!["title"] as! String!
let ownerId = owner!["id"] as! Int!
let distanceToTool = JSON[i].objectForKey("distance") as! Double
var description: String
if let des = tool!["description"] as? NSNull {
description = ""
} else {
description = (tool!["description"] as? String!)!
}
let myTool = Tool(title: title!, description: description, ownerId: ownerId!, distance: distanceToTool)
self.tools.append(myTool)
}
} else {
print("Sent search term, but no response")
}
self.refresh(self)
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return tools.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "ToolTableViewCell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! ToolTableViewCell
let tool = tools[indexPath.row]
cell.title?.text = tool.title
// print(tool.title)
// cell.toolListDescription?.text = tool.description
cell.ownerId = tool.ownerId
// print(tool.ownerId)
// print(tool.distance)
// cell.distance?.text = "\(tool.distance)mi"
print(cell)
return cell
print(cell)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
The call to reloadData belongs inside the completion closure for the Alamofire request.
Where have it now it's getting called before the Alamofire request call is complete.
I don't remember if Alamofire's completion closures are run on the main thread or on a background thread. If they're run on a background thread then you'll need to wrap the call to reloadData in a dispatch_async(dispatch_get_main_queue){ } call.
EDIT:
I said the call to reloadData belongs inside the closure for the Alamofire request call. Like this:
Alamofire.request(.GET, "http://localhost:3000/tools/search", parameters: ["keyword": searchTerm, "latitude": currentLat, "longitude": currentLong,
"user": userid]) .responseJSON {response in
if let JSON = response.result.value {
print("\(JSON)")
for i in 0 ..< JSON.count {
let owner = JSON[i].objectForKey("owner")
let tool = JSON[i].objectForKey("tool")
let title = tool!["title"] as! String!
let ownerId = owner!["id"] as! Int!
let distanceToTool = JSON[i].objectForKey("distance") as! Double
var description: String
if let des = tool!["description"] as? NSNull {
description = ""
} else {
description = (tool!["description"] as? String!)!
}
let myTool = Tool(title: title!, description: description, ownerId: ownerId!, distance: distanceToTool)
self.tools.append(myTool)
}
//This call is inside the closure for the request call.
dispatch_async(dispatch_get_main_queue())
{
//Call reloadData from the main thread
self.tableView.reloadData()
//Or you could call your refresh method.
}
} else {
print("Sent search term, but no response")
}
}
}