Double variable in MKMapItem array
Updated so the sample deduplicates by name:
var seenNames = Set<String>()
for (index , name) in response.mapItems.enumerated() {
let item = response.mapItems[index]
if(checkIfDatabaseGotThis(key: String(name.name!)) != nil && !seenNames.contains(item.name)){
self.matchingItems.append(item)
seenNames.insert(item.name)
self.tableView.reloadData()
}
}
That should remove all duplicates from your list of items based on the name. It keeps track of all the existing names that you have seen. If the name hasn't been seen before, the item is added to the list. Otherwise it is ignored.
Swift Organize UITableView by Users Distance from MKMapItems
Sort them by distance:
func sortedMapItems() -> [MKMapItem] {
return self.mapItems.sorted(by: { (a, b) -> Bool in
return self.userLocation.location!.distance(from: a.placemark.location!) >
self.userLocation.location!.distance(from: b.placemark.location!)
})
}
EDIT: Create a function to sort your mapitems then call it in viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
self.sortMapItems()
}
func sortMapItems() {
self.mapItems = self.mapItems.sorted(by: { (a, b) -> Bool in
return self.userLocation.location!.distance(from: a.placemark.location!) > self.userLocation.location!.distance(from: b.placemark.location!)
})
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return mapItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "resultCell", for: indexPath) as! ListedTableViewCell
// Configure the cell...
let row = indexPath.row
let item = mapItems[row]
cell.nameLabel.text = item.name
cell.detailLabel.text = item.phoneNumber
let distanceInMeters : Double = self.userLocation.location!.distance(from: mapItems[row].placemark.location!)
let distanceInMiles : Double = ((distanceInMeters.description as String).doubleValue * 0.00062137)
cell.distanceLabel.text = "\(distanceInMiles.string(2)) miles away"
return cell
}
Calling the original function (sortedMapItems) in this answer in cellForRowRowAtIndexPath
will be too heavy and unnecessary because the function will be called repeatedly.
A better approach would be re-create your data structure and add the distance of each items from user's location so that you won't have to call distance
function again in your cellForRow..
Using MKLocalSearch CompletionHandler correctly
The problem would be that search.start is ASYNCHRONOUS, it will start the request and return before results are done. Assume you need to work in completion handler.
Replace:
@IBAction func searchButtonPressed(_ sender: Any) {
search()
print(self.mapItems)
//chooseRandomSearchResult(results: self.mapItems!)
}
To: (Remove the usage of the results as they are not there yet, literally the search has been fired but has not returned)
@IBAction func searchButtonPressed(_ sender: Any) {
search()
}
AND do the actual work in the callback:
//Still inside search()
search.start { (response, error) in
//Executed after search() has already returned
print(response?.mapItems)
self.mapItems = response?.mapItems
chooseRandomSearchResult(results: self.mapItems!)
}
//Still inside search()
As you can see the code marked:
//Executed after search() has already returned
ALWAYS executes after //Still inside search()
even if it is before it in the function
(As of iOS 11.x, the documentation guarantees the completion handler for MKLocalSearch.start will be on the main thread)
Accessing MKLocalSearchResponse item (swift)
The answer is:
var newRecordAddress = (localSearchResponse.mapItems[0] as! MKMapItem).placemark
This object contains all information you need. Checked it in demo project
Address only:
var newRecordAddress = (localSearchResponse.mapItems[0] as! MKMapItem).placemark
let addressOnly = newRecordAddress.name + ", " + newRecordAddress.title
newRecordAddress.name
is place's namenewRecordAddress.title
is place's address you required
Cannot wait for the result of MKDirections.calculate, getting nil instead of it
Using MapKit & Swift 5
Calculate distance between two location location, It will help anyone
Sample Function : I have tested in Google Map as well as Apple Map
let startLocation : CLLocation = CLLocation.init(latitude: 23.0952779, longitude: 72.5274129)
let endLocation : CLLocation = CLLocation.init(latitude: 23.0981711, longitude: 72.5294229)
let distance = startLocation.distance(from: endLocation)
self.getDistance(departureDate: Date().adjust(hour: 8, minute: 0, second: 0, day: 0, month: 0), arrivalDate: Date().adjust(hour: 8, minute: 10, second: 0, day: 0, month: 0), startLocation: startLocation, endLocation: endLocation) { (distanceInMeters) in
print("fake distance: \(distance)")
let fakedistanceInMeter = Measurement(value: distance, unit: UnitLength.meters)
let fakedistanceInKM = fakedistanceInMeter.converted(to: UnitLength.kilometers).value
let fakedistanceInMiles = fakedistanceInMeter.converted(to: UnitLength.miles).value
print("fakedistanceInKM :\(fakedistanceInKM)")
print("fakedistanceInMiles :\(fakedistanceInMiles)")
print("actualDistance : \(distanceInMeters)")
let distanceInMeter = Measurement(value: distanceInMeters, unit: UnitLength.meters)
let distanceInKM = distanceInMeter.converted(to: UnitLength.kilometers).value
let distanceInMiles = distanceInMeter.converted(to: UnitLength.miles).value
print("distanceInKM :\(distanceInKM)")
print("distanceInMiles :\(distanceInMiles)")
}
Use of functions
self.getDistance(departureDate: trip.departure.dateTime, arrivalDate: trip.arrival.dateTime, startLocation: startLocation, endLocation: endLocation) { (actualDistance) in
print("actualDistance : \(actualDistance)")
}
I am improved above function and added code here, I hope it will help someone.
func calculateDistancefrom(departureDate: Date, arrivalDate: Date, sourceLocation: MKMapItem, destinationLocation: MKMapItem, doneSearching: @escaping (_ distance: CLLocationDistance) -> Void) {
let request: MKDirections.Request = MKDirections.Request()
request.departureDate = departureDate
request.arrivalDate = arrivalDate
request.source = sourceLocation
request.destination = destinationLocation
request.requestsAlternateRoutes = true
request.transportType = .automobile
let directions = MKDirections(request: request)
directions.calculate { (directions, error) in
if var routeResponse = directions?.routes {
routeResponse.sort(by: {$0.expectedTravelTime <
$1.expectedTravelTime})
let quickestRouteForSegment: MKRoute = routeResponse[0]
doneSearching(quickestRouteForSegment.distance)
}
}
}
func getDistance(departureDate: Date, arrivalDate: Date, startLocation : CLLocation, endLocation : CLLocation, completionHandler: @escaping (_ distance: CLLocationDistance) -> Void) {
let destinationItem = MKMapItem(placemark: MKPlacemark(coordinate: startLocation.coordinate))
let sourceItem = MKMapItem(placemark: MKPlacemark(coordinate: endLocation.coordinate))
self.calculateDistancefrom(departureDate: departureDate, arrivalDate: arrivalDate, sourceLocation: sourceItem, destinationLocation: destinationItem, doneSearching: { distance in
completionHandler(distance)
})
}
Route not showing in MKMapView?
Actually both source and destination variables were nil.. So i got bad response from the server.If you need just try the below code
func addRoutesOverLayForMapView(){
var source:MKMapItem?
var destination:MKMapItem?
var sourcePlacemark = MKPlacemark(coordinate: pickUpDistanceLocation!.coordinate, addressDictionary: nil)
source = MKMapItem(placemark: sourcePlacemark)
var desitnationPlacemark = MKPlacemark(coordinate: dropOffDistanceLocation!.coordinate, addressDictionary: nil)
destination = MKMapItem(placemark: desitnationPlacemark)
let request:MKDirectionsRequest = MKDirectionsRequest()
request.setSource(source)
request.setDestination(destination)
request.transportType = MKDirectionsTransportType.Walking
let directions = MKDirections(request: request)
directions.calculateDirectionsWithCompletionHandler ({
(response: MKDirectionsResponse?, error: NSError?) in
if error == nil {
self.showRoute(response!)
}
else{
println("trace the error \(error?.localizedDescription)")
}
})
}
func showRoute(response:MKDirectionsResponse){
for route in response.routes as! [MKRoute]{
mapView.addOverlay(route.polyline, level: MKOverlayLevel.AboveRoads)
var routeSeconds = route.expectedTravelTime
let routeDistance = route.distance
println("distance between two points is \(routeSeconds) and \(routeDistance)")
}
}
And you should implement this delegate method,dont forget to set the mapview delegate
func mapView(mapView: MKMapView!, rendererForOverlay overlay: MKOverlay!) -> MKOverlayRenderer! {
if overlay is MKPolyline {
var polylineRenderer = MKPolylineRenderer(overlay: overlay)
polylineRenderer.lineDashPattern = [14,10,6,10,4,10]
polylineRenderer.strokeColor = UIColor(red: 0.012, green: 0.012, blue: 0.012, alpha: 1.00)
polylineRenderer.lineWidth = 2.5
return polylineRenderer
}
return nil
}
Swift How to build a route FROM map Item, which I define in Search Bar
So I created an Array of mapItems, stored mapItems in it when table row is selected. Then when calling my buttons, using that array, I've assigned values to my source and destination points.
Related Topics
Implement a Custom Staggeregrid in UIview Like Etsy App in Swift
How to Calculate a Distance Between Two Anchorentities
Cloudkit - What to Do When a User Adds, Modifies or Deletes an Object While Offline
Create Skspritenode with an Asset Programmatically
How to Set Window Level in Swift
How to Disabled Some Default Functionality in Scene View When Allowscameracontrol = True
iOS 10. Coredata Insert New Object Sig Abrt
Playing Multiple Wav Out Multiple Channels Avaudioengine
Swift: Draw a Semi-Sphere in Mkmapview
Automatic Select a Date in Datepicker in Swift Language
Turn Off Touch for Whole Screen, Spritekit, How
Possible Bug? I Can Create Generic Instance Ignoring Constraint
Swift's Decimal Precision Issue
How to Collect The Return Value of a Function (Swift 3)
Trim Video Always Fail When Use Avassetexportpresetpassthrough