Не могу дождаться результата MKDirections.calculate, получая ноль вместо него
Я хотел бы рассчитать реальное расстояние между моим текущим местоположением и списком CLLocation
с помощью MKDirections.calculate
, Однако по какой-то причине команда возврата в конце функции не ждет результата и пытается вернуть пустую переменную. Мой код выглядит так:
func getDistance (location1: CLLocation, location2: CLLocation) {
let coordinates1 = location1.coordinate
let placemark1 = MKPlacemark(coordinate: coordinates1)
let sourceItem = MKMapItem(placemark: placemark1)
let coordinates2 = location2.coordinate
let placemark2 = MKPlacemark(coordinate: coordinates2)
let destinationItem = MKMapItem(placemark: placemark2)
let request = MKDirectionsRequest()
request.source = sourceItem
request.destination = destinationItem
request.requestsAlternateRoutes = true
request.transportType = .walking
var distance: Double?
let directions = MKDirections(request: request)
directions.calculate { (response, error) in
if var routeResponse = response?.routes {
routeResponse.sort(by: {$0.expectedTravelTime < $1.expectedTravelTime})
let quickestRoute: MKRoute = routeResponse[0]
distance = Double(quickestRoute.distance)
}
}
return distance //returns nil
}
И после этого я хотел бы использовать функцию в коде, подобном этому:
let myLocation = CLLocation(latitude: 47.0, longitude: 17.0)
let destinationArray = [CLLocation(latitude: 47.1, longitude: 17.1), CLLocation(latitude: 47.2, longitude: 17.2), CLLocation(latitude: 47.3, longitude: 17.3)]
var distanceArray: [Double] = []
for destination in destinationArray {
distanceArray.append(getDistance(location1: myLocation, location2: destination))
}
return distanceArray
Я пробовал замыкания, но они не работали, потому что я не смог найти способ вернуть distanceArray (та же ошибка, он не дождался выполнения замыкания и вернул пустой массив). Я также пробовал DispatchGroups, но они не имели никакого эффекта (возможно, я реализовал их неправильно).
Буду очень признателен за вашу помощь.
Спасибо.
2 ответа
Использование MapKit и Swift 5
Рассчитайте расстояние между двумя местоположениями, это поможет любому
Пример функции: я тестировал в Google Map, а также в 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)")
}
Использование функций
self.getDistance(departureDate: trip.departure.dateTime, arrivalDate: trip.arrival.dateTime, startLocation: startLocation, endLocation: endLocation) { (actualDistance) in
print("actualDistance : \(actualDistance)")
}
Я улучшил функцию выше и добавил сюда код, надеюсь, это кому-то поможет.
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)
})
}
Попробуйте следующее закрытие:
func getDistance (location1: CLLocation, location2: CLLocation,
completion: @escaping(Double?) -> Void) {
let coordinates1 = location1.coordinate
let placemark1 = MKPlacemark(coordinate: coordinates1)
let sourceItem = MKMapItem(placemark: placemark1)
let coordinates2 = location2.coordinate
let placemark2 = MKPlacemark(coordinate: coordinates2)
let destinationItem = MKMapItem(placemark: placemark2)
let request = MKDirectionsRequest()
request.source = sourceItem
request.destination = destinationItem
request.requestsAlternateRoutes = true
request.transportType = .walking
var distance: Double?
let directions = MKDirections(request: request)
directions.calculate { (response, error) in
if var routeResponse = response?.routes {
routeResponse.sort(by: {$0.expectedTravelTime < $1.expectedTravelTime})
let quickestRoute: MKRoute = routeResponse[0]
distance = Double(quickestRoute.distance)
completion(distance)
}
}
}
Использование:
let myLocation = CLLocation(latitude: 47.0, longitude: 17.0)
let destinationArray = [CLLocation(latitude: 47.1, longitude: 17.1), CLLocation(latitude: 47.2, longitude: 17.2), CLLocation(latitude: 47.3, longitude: 17.3)]
var distanceArray: [Double] = []
for destination in destinationArray {
getDistance(location1: myLocation, location2: destination) { distance in
print("distance", distance)
if let distance = distance {
distanceArray.append(distance)
}
}
}
return distanceArray
directions.calculate
является асинхронной функцией, поэтому вам нужно дождаться возврата функции, прежде чем возвращать вычисленное расстояние. Вы должны сделать это, используя обработчик завершения...
func getDistance(location1: CLLocation, location2: CLLocation, completion: @escaping (Double) -> Void) {
// etc
directions.calculate { (response, error) in
if var routeResponse = response?.routes {
routeResponse.sort(by: {$0.expectedTravelTime < $1.expectedTravelTime})
let quickestRoute: MKRoute = routeResponse[0]
completion(Double(quickestRoute.distance))
}
}
}
и тогда вы должны сделать что-то подобное с вашим массивом расстояний. Вам нужно будет подождать все getDistance
звонки, чтобы вернуться, так что вы можете использовать DispatchGroup
для этого…
func getDistanceArray(completion: @escaping ([Double]) -> Void) {
let group = DispatchGroup()
var distanceArray: [Double] = []
for destination in destinationArray {
group.enter()
getDistance(location1: myLocation, location2: destination) { distance in
distanceArray.append(distance)
group.leave()
}
}
group.wait()
completion(distanceArray)
}
который вы можете назвать...
getDistanceArray { distanceArray in
print(distanceArray)
}