使用Google Maps API在Swift 3中查找两个位置之间的驾车/步行距离

3

我正在使用Google Maps API获取两个位置之间的驾车/步行距离。我正在将我的应用从objective-c更新到Swift 3,目前卡在将这段代码转换为Swift 3上。

NSString *dist;
NSString *strUrl = [NSString stringWithFormat:@"http://maps.googleapis.com/maps/api/directions/json?origin=%f,%f&destination=%f,%f&sensor=false&mode=%@", closestLocation.coordinate.latitude,  closestLocation.coordinate.longitude, _currentUserLocation.coordinate.latitude,  _currentUserLocation.coordinate.longitude, @"DRIVING"];
NSURL *url = [NSURL URLWithString:[strUrl stringByAddingPercentEncodingWithAllowedCharacters:NSCharacterSet.URLQueryAllowedCharacterSet]];
NSData *jsonData = [NSData dataWithContentsOfURL:url];
if(jsonData != nil)
{
    NSError *error = nil;
    id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error];
    NSMutableArray *arrDistance=[result objectForKey:@"routes"];
    if ([arrDistance count]==0) {
        NSLog(@"N.A.");
    }
    else{
        NSMutableArray *arrLeg=[[arrDistance objectAtIndex:0]objectForKey:@"legs"];
        NSMutableDictionary *dictleg=[arrLeg objectAtIndex:0];

        dist = [NSString stringWithFormat:@"%@",[[dictleg   objectForKey:@"distance"] objectForKey:@"text"]];
    }
}
else{
    NSLog(@"N.A.");
}

我尝试使用Swiftify等工具,但到处都出现错误。目前我拥有的是:

var dist: String = ""
var strUrl: String = "http://maps.googleapis.com/maps/api/directions/json?origin=\(closestLocation!.coordinate.latitude),\(closestLocation!.coordinate.longitude)&destination=\(currentUserLocation!.coordinate.latitude),\(currentUserLocation!.coordinate.longitude)&sensor=false&mode=\("DRIVING")"
var url = URL(string: strUrl)
var jsonData = Data(contentsOf: url!)
    if (jsonData != nil) {
        var error: Error? = nil
        var result: Any? = try? JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.Element.mutableContainers)
        var arrDistance: [Any]? = (result?["routes"] as? [Any])
        if arrDistance?.count == 0 {
            print("N.A.")
        }
        else {
            var arrLeg: [Any]? = ((arrDistance?[0] as? [Any])?["legs"] as? [Any])
            var dictleg: [AnyHashable: Any]? = (arrLeg?[0] as? [AnyHashable: Any])

            dist = "\(dictleg?["distance"]["text"])"
        }
    }
    else {
        print("N.A.")
    }

我现在遇到的错误如下:

使用Data(contentsOf: url!)时,它告诉我“调用可能会抛出异常,但未标记为'try'并且错误未处理”

它不喜欢我将[任何]用于我的arrDistance变量。

如果有人知道如何在Swift 3中实现此API调用,那将非常有帮助。谢谢


1
与问题无关,但您不应使用Data(contentsOf: url!)来下载数据。您应该使用URLSession dataTask(with: URL) - Leo Dabus
但是为什么你要使用谷歌的API而不是苹果内部的呢? - ScottyBlades
2个回答

6
编译器提示Data(contentsOf:)可能会throws异常,因此您需要用do catch块进行处理。如评论中建议,您需要使用dataTask(with:)代替Data(contentsOf:)来下载数据。您还可以使用带有代码的完成块,因此请像以下示例中更改您的代码。
func getDistance(completion: @escaping(String) -> Void) {

    var dist = ""
    let strUrl = "http://maps.googleapis.com/maps/api/directions/json?origin=\(closestLocation!.coordinate.latitude),\(closestLocation!.coordinate.longitude)&destination=\(currentUserLocation!.coordinate.latitude),\(currentUserLocation!.coordinate.longitude)&sensor=false&mode=\("DRIVING")"
    let url = URL(string: strUrl)!
    let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) in
        guard let data = data, error == nil else {
            print(error?.localizedDescription ?? "")
            completion(dist)
            return
        }
        if let result = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String:Any],
            let routes = result["routes"] as? [[String:Any]], let route = routes.first,
            let legs = route["legs"] as? [[String:Any]], let leg = legs.first,
            let distance = leg["distance"] as? [String:Any], let distanceText = distance["text"] as? String {

            dist = distanceText
        }
        completion(dist)
    })
    task.resume()
}

现在只需按照以下方式调用 getDistance 函数即可。
self.getDistance { (distance) in
    //Update UI on main thread
    DispatchQueue.main.async {
        print(distance)
        self.label.text = distance
    }
}

但是我遇到了一个问题,无法存储该距离变量的值。我正在尝试将该值放在我的视图控制器之一的标签上。 - trludt
1
@trludt,你是否可以在getDistance完成后将标签文字设置为Label文本,而不是使用print语句,像这样:self.getDistance { (distance) in yourLabel.text = distance } - Nirav D
标签显示值需要一段时间。虽然技术上可行,但我需要在视图加载时立即显示该信息。 - trludt
@trludt 完成块的调用取决于您的网络速度,但如果您收到响应后仍然需要时间来更新UI,则应在主线程上进行UI更改,因此将UI更改代码放在DispatchQueue.main.async { //在这里进行ui更改 }中。 - Nirav D
所以我将self.getDistance函数放在DispatchQueue.main.async {}中,并通过那里更新UI,但仍需要近20-30秒才能更新。 - trludt
@trludt 不要把 self.getDistance 放在主线程里,看一下我编辑过的答案,并在主线程上调用 UI 更改。 - Nirav D

0

Swift 5+:
如果有人正在寻找答案,可以使用CoreLocation。
据我所知,有两种方法可以找到距离。 如果您正在寻找驾车距离,您可以始终使用MKDirections。 以下是查找驾车距离的代码(您也可以通过更改交通方式来查找步行和公共交通距离)。

let sourceP = CLLocationCoordinate2DMake( sourceLat, sourceLong)
let destP = CLLocationCoordinate2DMake( desLat, desLong)
let source = MKPlacemark(coordinate: sourceP)
let destination = MKPlacemark(coordinate: destP)
        
let request = MKDirections.Request()
request.source = MKMapItem(placemark: source)
request.destination = MKMapItem(placemark: destination)

// Specify the transportation type
request.transportType = MKDirectionsTransportType.automobile;

// If you want only the shortest route, set this to a false
request.requestsAlternateRoutes = true

let directions = MKDirections(request: request)

 // Now we have the routes, we can calculate the distance using
 directions.calculate { (response, error) in
    if let response = response, let route = response.routes.first {
                print(route.distance) //This will return distance in meters
    }
 }

如果您只需要空气距离/鸟瞰距离/坐标距离,您可以使用以下代码:
let sourceP = CLLocation(latitude: sourceLat, longitude: sourceLong)
let desP = CLLocation(latitude: desLat, longitude: desLong))

let distanceInMeter = sourceP.distance(from: desP)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接