【问题标题】:How to search MKMapView with UISearchBar?如何使用 UISearchBar 搜索 MKMapView?
【发布时间】:2011-01-17 21:59:38
【问题描述】:

我有一个应用程序需要具有类似的搜索功能,例如 Apple“地图”应用程序(包括在 iPhone、iPod Touch 和 iPad 中)。

有问题的功能应该不难做到,但我真的不知道如何在搜索栏中输入街道地址,然后获取该地址的坐标或可以帮助我实际移动的东西那个地方的地图和中心。

我的意思是,我要查询什么,Apple 是否提供了“地址搜索 API 方法”?还是我需要直接使用谷歌地图 API?

我很想听听应该怎么做。

【问题讨论】:

    标签: iphone ios cocoa-touch mkmapview mkreversegeocoder


    【解决方案1】:

    这可能是最简单的方法。它使用苹果服务器进行地理编码。有时苹果服务器提供比谷歌更好的响应。很快(在 IOS 6.1 中)谷歌地图将完全脱离 IOS。因此,如果应用程序保留在苹果提供的功能中,那就太好了。

    -(void)searchBarSearchButtonClicked:(UISearchBar *)theSearchBar
    {
        [theSearchBar resignFirstResponder];
        CLGeocoder *geocoder = [[CLGeocoder alloc] init];
        [geocoder geocodeAddressString:theSearchBar.text completionHandler:^(NSArray *placemarks, NSError *error) {
            //Error checking
    
            CLPlacemark *placemark = [placemarks objectAtIndex:0];
            MKCoordinateRegion region;
            region.center.latitude = placemark.region.center.latitude;
            region.center.longitude = placemark.region.center.longitude;
            MKCoordinateSpan span;
            double radius = placemark.region.radius / 1000; // convert to km
    
            NSLog(@"[searchBarSearchButtonClicked] Radius is %f", radius);
            span.latitudeDelta = radius / 112.0;
    
            region.span = span;
    
            [theMapView setRegion:region animated:YES];
        }];
    }
    

    【讨论】:

    • 请注意 CLGeocoder 仍处于起步阶段,在撰写本文时似乎不会返回多个 placemark 值。尽管它看起来会在未来超越 Google Maps API 选项,但一切都保持不变
    • 此解决方案在 iphone 6.0 上运行良好,但我在 iphone 5.0 和 5.1 上遇到了 setRegion 方法的问题。我收到以下异常:'NSInvalidArgumentException',原因:'Invalid Region '。解决方案是在设置区域属性后添加以下行: region = [theMapView regionThatFits:region];
    • FWIW 地标可能没有设置区域,所以上面的代码可能会崩溃。如果 placemark.region 为零,只需使用另一个半径 - 比如说 500 米。除此之外,是的,这是要走的路。
    • 这个答案使用了一些不推荐使用的属性,这些属性不允许在 Swift 中访问。有人有最新的方法吗?
    【解决方案2】:

    好的,回答我自己的问题:

    如前所述,最好的办法是使用 Google Maps API, 它支持很多格式,但出于几个原因,我选择使用 JSON。

    以下是对 Google 地图执行 JSON 查询并获取查询坐标的步骤。请注意,并非所有正确的验证都已完成,这只是一个概念证明。

    1) 为 iPhone 下载一个 JSON 框架/库,有好几个,我选择了this one,它非常好,似乎是一个活跃的项目,加上几个商业应用程序似乎正在使用它。所以把它添加到你的项目中(指令here)。

    2) 要在 Google 地图中查询地址,我们需要构建一个请求 URL,如下所示: http://maps.google.com/maps/geo?q=Paris+France

    此 url 将为查询“Paris+France”返回一个 JSON 对象。

    3) 代码:

    //Method to handle the UISearchBar "Search", 
    - (void) searchBarSearchButtonClicked:(UISearchBar *)theSearchBar 
    {
        //Perform the JSON query.
        [self searchCoordinatesForAddress:[searchBar text]];
    
        //Hide the keyboard.
        [searchBar resignFirstResponder];
    }
    

    处理完 UISearchBar 搜索后,我们必须向 Google 地图发出请求:

    - (void) searchCoordinatesForAddress:(NSString *)inAddress
    {
        //Build the string to Query Google Maps.
        NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@?output=json",inAddress];
    
        //Replace Spaces with a '+' character.
        [urlString setString:[urlString stringByReplacingOccurrencesOfString:@" " withString:@"+"]];
    
        //Create NSURL string from a formate URL string.
        NSURL *url = [NSURL URLWithString:urlString];
    
        //Setup and start an async download.
        //Note that we should test for reachability!.
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
        NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    
        [connection release];
        [request release];
    }
    

    我们当然必须处理 GoogleMaps 服务器的响应(注意:缺少很多验证)

    //It's called when the results of [[NSURLConnection alloc] initWithRequest:request delegate:self] come back.
    - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
    {   
        //The string received from google's servers
        NSString *jsonString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
        //JSON Framework magic to obtain a dictionary from the jsonString.
        NSDictionary *results = [jsonString JSONValue];
    
        //Now we need to obtain our coordinates
        NSArray *placemark  = [results objectForKey:@"Placemark"];
        NSArray *coordinates = [[placemark objectAtIndex:0] valueForKeyPath:@"Point.coordinates"];
    
        //I put my coordinates in my array.
        double longitude = [[coordinates objectAtIndex:0] doubleValue];
        double latitude = [[coordinates objectAtIndex:1] doubleValue];
    
        //Debug.
        //NSLog(@"Latitude - Longitude: %f %f", latitude, longitude);
    
        //I zoom my map to the area in question.
        [self zoomMapAndCenterAtLatitude:latitude andLongitude:longitude];
    
        [jsonString release];
    }
    

    最后是缩放我的地图的功能,现在这应该是一件小事。

    - (void) zoomMapAndCenterAtLatitude:(double) latitude andLongitude:(double) longitude
    {
        MKCoordinateRegion region;
        region.center.latitude  = latitude;
        region.center.longitude = longitude;
    
        //Set Zoom level using Span
        MKCoordinateSpan span;
        span.latitudeDelta  = .005;
        span.longitudeDelta = .005;
        region.span = span;
    
        //Move the map and zoom
        [mapView setRegion:region animated:YES];
    }
    

    希望这对某人有所帮助,因为 JSON 部分很难弄清楚,我认为该库的文档记录不是很好,但它仍然非常好。

    编辑:

    由于@Leo 的问题,将一个方法名称修改为“searchCoordinatesForAddress:”。我不得不说,这种方法作为概念证明很好,但是如果您打算下载大的 JSON 文件,则必须附加到 NSMutableData 对象以将所有查询保存到 google 服务器。 (请记住,HTTP 查询是零散的。)

    【讨论】:

    • 您好,但如果我正确阅读您的代码,您无法解决给定地址上有多个结果的情况。
    • 是的,但是考虑到这个解决方案应该不难解决。
    • 我可以建议使用 stringByAddingPercentEscapesUsingEncoding: 方法而不是用“+”替换空格吗? :)
    • 一个建议:使用http://maps.google.com/maps/geo?q=%@&output=csv(以CSV模式输出)避免解析JSON。 (无需依赖 JSON 框架)
    • 好吧,从技术上讲,对于 iOS 5+,JSON 解析是 Cocoa-Touch 的一部分 :) 所以也没有那么糟糕。
    【解决方案3】:

    如果您搜索某个地区,此链接会为您提供帮助。

    NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@?output=json",inAddress];
    

    如果你想搜索一条街道,这是正确的链接

    NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.google.com/maps/geo?q=%@&output=json",inAddress];
    

    注意第二个?应该是&

    【讨论】:

    • 我花了一段时间才发现这两个字符串之间的区别,但是“&”而不是“?”非常有帮助,谢谢!
    【解决方案4】:

    Swift 版本,适配 iOS 9:

    let geocoder = CLGeocoder()
    geocoder.geocodeAddressString(addressString) { (placemarks, error) in
    
        if let center = (placemarks?.first?.region as? CLCircularRegion)?.center {
    
            let region = MKCoordinateRegion(center: center, span: MKCoordinateSpanMake(0.02, 0.02))
            self.mapView.setRegion(region, animated: true)
        }
    }
    

    基于 user1466453 的回答。

    【讨论】:

      【解决方案5】:

      如果其他人遇到同样的问题,请点击以下链接: https://github.com/stig/json-framework/ 向下滚动到 项目重命名为 SBJson

      此外,这里是在您的应用使用之前获取所有数据的代码。请注意,委托方法“确实收到了数据”,因为它将可变数据对象附加到下载的数据中。

      我刚刚使用了 MR GANDOS searchCoodinatesMETHOD,因为它运行良好

      - (void) searchCoordinatesForAddress:(NSString *)inAddress
      {
          //Build the string to Query Google Maps.
          NSMutableString *urlString = [NSMutableString stringWithFormat:@"http://maps.googleapis.com/maps/api/geocode/json?address=%@&sensor=false",inAddress];
      
          //Replace Spaces with a '+' character.
          [urlString setString:[urlString stringByReplacingOccurrencesOfString:@" " withString:@"+"]];
      
          //Create NSURL string from a formate URL string.
          NSURL *url = [NSURL URLWithString:urlString];
      
          //Setup and start an async download.
          //Note that we should test for reachability!.
          NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
      
          NSURLConnection *connection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
      
          [connection release];
          [request release];
      }
      

      // 第一步 // 这很重要,因为它会在收到响应后立即创建可变数据对象

      -(void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response
      {
          if (receivedGeoData) 
          {
              [receivedGeoData release];
              receivedGeoData = nil;
              receivedGeoData = [[NSMutableData alloc] init];
          }
          else
          {
              receivedGeoData = [[NSMutableData alloc] init];
          }
      
      }
      

      /// 第二步 // 这个很重要,因为它会在数据对象中附加数据

      - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
      {   
          [receivedGeoData appendData:data]; 
      }
      

      // 第三步...... // 现在您已经使用了所有数据

      - (void)connectionDidFinishLoading:(NSURLConnection *)connection
      {
          NSString *jsonResult = [[NSString alloc] initWithData:receivedGeoData encoding:NSUTF8StringEncoding];
          NSError *theError = NULL;
          dictionary = [NSMutableDictionary dictionaryWithJSONString:jsonResult error:&theError];
      
          NSLog(@"%@",dictionary);
      
          int numberOfSites = [[dictionary objectForKey:@"results"] count];
          NSLog(@"count is %d ",numberOfSites);      
      }
      
      -(void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error
      {
          // Handle the error properly
      }
      

      【讨论】:

        【解决方案6】:

        您可以使用 Google 的 API 服务从文本搜索字符串中获取纬度/经度坐标。请务必传递用户的当前位置,以便结果相关。阅读这个问题的答案:Search and display business locations on MKMapView

        【讨论】:

          猜你喜欢
          • 2011-07-27
          • 1970-01-01
          • 2011-08-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多