How to select and display specific information in a TableView Cells? - ios

Hello I need help to select and display only the fruits which are 5km from the user's current location. I have to display them in table view cells.Here is my code:
#IBOutlet weak var LabelTest: UILabel!
var locationManager = CLLocationManager()
var lat = Double()
var long = Double()
var latitude = Double()
var longitude = Double()
struct Fruit {
let name : String
let location : CLLocation
let latitude : Double
let longitude: Double
let imageURL : NSURL
let description : String
func distanceTo(location:CLLocation) -> Int
{
let distanceMeters = location.distanceFromLocation(self.location)
let distanceKilometers = distanceMeters / 1000.00
return Int(round(100 * distanceKilometers) / 100)
}
}
var fruits = [Fruit]()
func parseFruits() {
if CalculateDistance() < 5 {
guard let url = NSBundle.mainBundle().URLForResource("cities", withExtension: "json"), jsonData = NSData(contentsOfURL: url) else {
print("Error finding JSON File")
return
}
let jsonObject = JSON(data: jsonData)
let fruitArray = jsonObject["fruits"].arrayValue
for aFruit in fruitArray {
let name = aFruit["Name"].stringValue
let latitude = aFruit["Latitude"] as! Double
let longitude = aFruit["Longitude"] as! Double
let location = CLLocation(latitude: latitude, longitude: longitude)
let imageURL = aFruit["Picture"].stringValue
let description = aFruit["Description"].stringValue
let fruit = Fruit(name: name,location: location,latitude:latitude,longitude: longitude, imageURL: NSURL(string:imageURL)!, description: description)
fruits.append(fruit)
}
self.tableView.reloadData()
}
}
override func viewDidLoad() {
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
super.viewDidLoad()
parseFruits()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location:CLLocationCoordinate2D = manager.location!.coordinate
lat = location.latitude
long = location.longitude
}
func CalculateDistance() -> Int{
let userLocation = CLLocation(latitude: lat, longitude: long)
let destinationLocation = CLLocation(latitude:latitude, longitude: longitude)// latitude and longitude from the json file
let distance = userLocation.distanceFromLocation(destinationLocation)
return roundToFive(distance)
}
private func roundToFive(x : Double) -> Int {
return 5 * Int(round(x / 5.0))
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruits.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! TableViewCell
let fruit = fruits[indexPath.row]
cell.CellTitle.text = fruit.name
cell.CellDescription.text = fruit.description
let image = UIImage(data: NSData(contentsOfURL:(string: fruit.imageURL))!)
cell.CellImage.image = image
return cell
}
In the TableView Cells I have to display only the fruits which are 5km from the user's location. I tried to do something, but it seems that it isn't working. Any ideas how to fix it?

Just do it like this:
// Update your CalculateDistance method to the following
func CalculateDistance(lat: Double, long: Double) -> Int{
let userLocation = CLLocation(latitude: lat, longitude: long)
let destinationLocation = CLLocation(latitude:latitude, longitude: longitude)// latitude and longitude from the json file
let distance = userLocation.distanceFromLocation(destinationLocation)
return roundToFive(distance)
}
// When you parse your fruits, you can make the check if it´s within 5km then you can add it to your array or add it to a separate array
if (CalculateDistance(latitude, longitude) <= 5000{
fruits.append(fruit)
}

What does your CalculateDistance method do? You should filter your array.
func shouldShowFruit(fruit: Fruit) -> Bool{
let userLocation = CLLocation(latitude: lat, longitude: long)
let destinationLocation = CLLocation(latitude:fruit.latitude, longitude: fruit.longitude)
let distance = userLocation.distanceFromLocation(destinationLocation)
return distance < 5000
}
Call this method for every item in your array and if it returns false remove from array.

Related

Displaying multiple driver annotations around the user (with Database)

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)
}
}
}

Annotations are not showing in MKMapView?

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.

Reloading UITableview on iOS

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.

How to compare user'r current location to other locations and display them in UITable View?

I am developing an app in swift and I have to compare user's current location to the other locations taken from a JSON file. Then I have to display all of the locations which are in a certain range from the user's location. This range I take from a UISlider. I mean when the user selects 25km in the slider, the app has to determine the current user's location and display all of the fruits which are in this range.
import UIKit
import SwiftyJSON
import MapKit
import CoreLocation
class TableViewController: UITableViewController,CLLocationManagerDelegate {
#IBOutlet weak var Bar: UIToolbar!
#IBOutlet weak var LabelTest: UILabel! // this is the slider value, I segue it from the previous viewcontroller
var manager = CLLocationManager()
struct Fruit {
let name : String
let location : CLLocation
let imageURL : NSURL
let description : String
}
var fruits = [Fruit]()
func parseFruits() {
guard let url = NSBundle.mainBundle().URLForResource("cities", withExtension: "json"), jsonData = NSData(contentsOfURL: url) else {
print("Error finding JSON File")
return
}
let jsonObject = JSON(data: jsonData)
let fruitArray = jsonObject["fruits"].arrayValue
for aFruit in fruitArray {
let name = aFruit["Name"].stringValue
let latitude = city["Latitude"] as! Double
let longitude = city["Longitude"] as! Double
let location = CLLocation(latitude: latitude, longitude: longitude)
let imageURL = aFruit["Picture"].stringValue
let description = aFruit["Description"].stringValue
let fruit = Fruit(name: name,location: location,imageURL: NSURL(string:imageURL)!, description: description )
fruits.append(fruit)
}
self.tableView.reloadData()
}
override func viewDidLoad() {
super.viewDidLoad()
parseFruits()
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return fruits.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: TableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as! TableViewCell
let fruit = fruits[indexPath.row]
cell.CellTitle.text = fruit.name
cell.CellDescription.text = fruit.description
let image = UIImage(data: NSData(contentsOfURL:(string: fruit.imageURL))!)
cell.CellImage.image = image
return cell
}
Currently I am not calculating the user's current location and I am not comparing it, I would be glad if someone can show me how to do that.
edited:
func CalculateDistance() {
let userLocation = CLLocation(latitude: lat, longitude: long)
let destinationLocation = CLLocation(latitude:latitude, longitude: longitude)// latitude and longitude from the json file
let distance = userLocation.distanceFromLocation(destinationLocation)
}
When you want to calculate the distance between two locations you can do the following:
let userLocation = CLLocation(latitude: lat, longitude: long)
let destinationLocation = CLLocation(latitude: (dest.lat as NSString).doubleValue, longitude: (dest.long as NSString).doubleValue)
let distance = userLocation.distanceFromLocation(destinationLocation)
Get the userLocation which is the current location of the user. Then you have the location of the destination and then calculate the distance with the help of the distanceFromLocation function which is a part of CoreLocation.
Then I have done a method that rounds the distance to nearest 5 meters:
var distanceToFive = roundToFive(distance)
private func roundToFive(x : Double) -> Int {
return 5 * Int(round(x / 5.0))
}
You can of course change this to 10, 20 etc.
Edit:
And to get the current location:
Add the CLLocationManagerDelegate to the class inheritance. Declare var locationManager = CLLocationManager() and two variables one for lat and one for long. In viewDidLoad do
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
}
And then to get the location for the user declare the following methods:
func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location:CLLocationCoordinate2D = manager.location!.coordinate
lat = location.latitude
long = location.longitude
}
func locationManager(manager: CLLocationManager, didFailWithError error: NSError) {
print("Error")
}
Edit2:
func CalculateDistance() -> Int{
let userLocation = CLLocation(latitude: lat, longitude: long)
let destinationLocation = CLLocation(latitude:latitude, longitude: longitude)// latitude and longitude from the json file
let distance = userLocation.distanceFromLocation(destinationLocation)
return roundToFive(distance)
}
private func roundToFive(x : Double) -> Int {
return 5 * Int(round(x / 5.0))
}

Swift-Xcode: why isn't my self.tableView.reloadData() call loading the promised data into my table / cells?

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")
}
}
}

Resources