kelsheikh
kelsheikh

Reputation: 1338

Distance between locations from an Array of coordinates into a table

Im trying to populate a table with rows that show distances to locations from the users current location from an Array of coordinates. I have the coordinates in an empty array and I have the users location.

Im stuck on the getting the coordinates array into a function to calculate the distance, and then putting each distance value into a label in table rows. Any insight is greatly appreciated.

var locationArray: [CLLocationCoordinate2D] = []
var shopperLocation = String()
var distanceText: [String] = []

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
    let userLocation:CLLocation = locations[0]

    shopperLocation = userLocation

    self.manager.stopUpdatingLocation()
}

//This is the function I am trying to use to get distance from the array of coordinates //

func distanceToLocation(){

    if locationArray == nil {

        locationArray = userLocation
    }

    let distanceBetween: CLLocationDistance =   shopperLocation.distanceFromLocation(startLocation) / 1609.34

    let distanceInMilesFromUser = String(format: "%.2f", distanceBetween)

    distanceText = "\(distanceInMilesFromUser) mi"
   }

  // Here I am trying to get the distance values in the correct rows //

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

    let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! listViewCell

    cell.myDistance.text = distanceText[indexPath.row]

    return cell
}

Upvotes: 2

Views: 1357

Answers (2)

Jean-Baptiste
Jean-Baptiste

Reputation: 967

This is because you have to explicitly tell the table view to reload the data, that is to ask again its TableViewDataSource to configure every cell with fresh data.

You can easily do so by calling tableView.reloadData() in your CLLocationManager's delegate method. The code below is one way to achieve that. By the way, a few remarks:

  • I find it more elegant to initialise Arrays using var myArray = [Type]() than providing an explicit type and then assigning an empty array

  • You better save your shops parameters (name, coordinates, etc.) in a struct

  • Keep track of the current user location instead of the distance; doing so will enable you to easily add new shops without requesting the user location once again

  • Use MKDistanceFormatter to get a nice human-readable text output for your distances — this is very powerful and will adapt to your user's locale and preferred unit system for free!

Do not forget to import all required modules:

import UIKit
import CoreLocation
import MapKit

Define your constants:

let locationCellIdentifier = "LocationCell"

and your struct:

struct Shop {
    let name: String
    let coordinates: CLLocation
}

And then your view controller:

final class LocationTableView: UITableViewController, CLLocationManagerDelegate {
    
    var shops = [Shop]()
    var userLocation: CLLocation?
    let distanceFormatter = MKDistanceFormatter()
    
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        
        // Save the location and reloads table view
        if !locations.isEmpty {
            userLocation = locations.first
            manager.stopUpdatingLocation()
            tableView.reloadData()
        }
    }
    
    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return shops.count
    }
    
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier(locationCellIdentifier, forIndexPath: indexPath)
        
        // Configure the cell
        let shop = shops[indexPath.row]
        cell.textLabel?.text = shop.name
        
        if let userLocation = userLocation {
            let distance = shop.coordinates.distanceFromLocation(userLocation)
            let formattedDistance = distanceFormatter.stringFromDistance(distance)
            cell.detailTextLabel?.text = formattedDistance
        } else {
            cell.detailTextLabel?.text = nil
        }
        
        return cell
    }
}

Upvotes: 3

nPn
nPn

Reputation: 16748

This seems kind of rough right now, so I will try to just give an outline based on what it seems like you are trying to do and the problem you seem to be having.

First you have a locationArray, but it's empty, I assume you will populate it some how.

You also have a distanceText array, so here is a suggested way you could go. (and none of this has been tested or verified so treat it more as psudeo code).

When your location is updated

  1. get the last location (most recent)
  2. map the current location array to a new distanceText array.
  3. ask the tableView to reload it's data.

    var locationArray: [CLLocationCoordinate2D] = [] 
    var distanceText: [String] = []
    
    func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        let userLocation = locations.last
        distanceText = locationArray.map { location in 
            // add your code to calculated the distance and return it
            let distanceAsText = .......  // some function of location and userLocation
            return distanceAsText
        }
        tableView?.reloadData()        
    }
    

Upvotes: 1

Related Questions