【问题标题】:MapView with local search带有本地搜索的 MapView
【发布时间】:2013-07-16 17:07:28
【问题描述】:

我有一个关于在我的应用中实现的 Apple 地图中进行本地搜索的问题。 该应用程序应该向用户显示他周围的“超市”在哪里。我的 mapView 还显示了用户的当前位置,但我真的不知道应该如何处理市场位置的结果。也许苹果为此提供了解决方案/建议。 提前致谢。

结果(超市)应该显示在这张图片中。

更新: 该功能完美运行,但是否也可以使这些pin 可点击以显示有关此位置的信息表,如下图所示?

更新 2: 我对这个地图实现非常着迷,所以我想知道是否也可以获取有关您当前位置的信息(如下面的屏幕截图所示)。

【问题讨论】:

    标签: ios location mkmapview mapkit


    【解决方案1】:

    在 iOS 6.1 及更高版本中,您可以使用 MKLocalSearch,为您找到的每个 MKMapItem 对象添加注释。

    MKLocalSearchRequest *request = [[MKLocalSearchRequest alloc] init];
    request.naturalLanguageQuery = @"supermarket";
    request.region = mapView.region;
    
    MKLocalSearch *search = [[MKLocalSearch alloc] initWithRequest:request];
    
    [search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
        for (MKMapItem *item in response.mapItems)
        {
            MKPointAnnotation *annotation = [[MKPointAnnotation alloc] init];
            annotation.coordinate = item.placemark.coordinate;
            annotation.title      = item.name;
            annotation.subtitle   = item.placemark.title;
            [mapView addAnnotation:annotation];
        }
    }];
    

    如果您想在标注上使用左右配件,您应该实现一个 viewForAnnotation 来添加这些配件(当然,要使其工作,您必须将您的控制器定义为您的控制器的 delegate MKMapView):

    typedef enum : NSInteger
    {
        kCallOutAccessoryRight = 1,
        kCallOutAccessoryLeft
    } CallOutAccessoryType;
    
    - (MKAnnotationView *)mapView:(MKMapView *)mapView viewForAnnotation:(id<MKAnnotation>)annotation
    {
        if ([annotation isKindOfClass:[MKUserLocation class]])
            return nil;
    
        static NSString *identifier = @"customAnnotationView";
    
        MKAnnotationView *annotationView = (MKAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:identifier];
    
        if (annotationView == nil)
        {
            annotationView = [[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:identifier];
            annotationView.canShowCallout = YES;
    
            annotationView.rightCalloutAccessoryView     = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
            annotationView.rightCalloutAccessoryView.tag = kCallOutAccessoryRight;
    
            annotationView.leftCalloutAccessoryView      = ...; // whatever you want for the left button
            annotationView.leftCalloutAccessoryView.tag  = kCallOutAccessoryLeft;
        }
        else
        {
            annotationView.annotation = annotation;
        }
    
        return annotationView;
    }
    

    而且,您可能想要响应用户点击这些标注:

    - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
    {
        if (control.tag == kCallOutAccessoryRight)
        {
            NSLog(@"Present info sheet for %@ here", [view.annotation title]);
        }
        else if (control.tag == kCallOutAccessoryLeft)
        {
            NSLog(@"Do whatever you want if left accessory tapped");
        }
    }
    

    您询问了如何显示用户路线。是的,您可以通过将MKMapItem 传递给openMapsWithItems 来做到这一点。但这假定您从本地搜索中保存了MKMapItem。为此,您将创建一个自定义注释。例如:

    //  MapItemAnnotation.h
    
    #import <Foundation/Foundation.h>
    #import <MapKit/MapKit.h>
    
    @interface MapItemAnnotation : NSObject <MKAnnotation>
    
    @property (nonatomic, strong, readonly) MKMapItem *item;
    
    - (id)initWithMapItem:(MKMapItem *)item;
    - (NSString *)title;
    - (NSString *)subtitle;
    - (CLLocationCoordinate2D)coordinate;
    
    @end
    

    //  MapItemAnnotation.m
    
    #import "MapItemAnnotation.h"
    
    @implementation MapItemAnnotation
    
    - (id)initWithMapItem:(MKMapItem *)item
    {
        self = [super init];
        if (self) {
            _item = item;
        }
        return self;
    }
    
    - (NSString *)title
    {
        return _item.name;
    }
    
    - (NSString *)subtitle
    {
        return _item.placemark.title;
    }
    
    - (CLLocationCoordinate2D)coordinate
    {
        return _item.placemark.coordinate;
    }
    
    @end
    

    完成后,您将注释添加到地图会稍微简化:

    [search startWithCompletionHandler:^(MKLocalSearchResponse *response, NSError *error) {
        for (MKMapItem *item in response.mapItems)
        {
            MapItemAnnotation *annotation = [[MapItemAnnotation alloc] initWithMapItem:item];
            [mapView addAnnotation:annotation];
        }
    }];
    

    但是,现在,您的左侧标注配件可以轻松地在 Apple 地图中启动路线:

    - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
    {
        if (control.tag == kCallOutAccessoryRight)
        {
            NSLog(@"Present info sheet for %@ here", [view.annotation title]);
        }
        else if (control.tag == kCallOutAccessoryLeft)
        {
            // request directions from Apple Maps
    
            MapItemAnnotation *annotation = view.annotation;
            NSAssert([annotation isKindOfClass:[MapItemAnnotation class]], @"Annotation should be MapItemAnnotation: %@", annotation);
    
            [annotation.item openInMapsWithLaunchOptions:@{MKLaunchOptionsMapCenterKey      : [NSValue valueWithMKCoordinate:mapView.region.center],
                                                           MKLaunchOptionsMapSpanKey        : [NSValue valueWithMKCoordinateSpan:mapView.region.span],
                                                           MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving}];
        }
    }
    

    关于更新用户位置描述,你可以定义一个didUpdateUserLocation方法(即每次用户位置变化时,地图视图都会调用这个MKMapViewDelegate方法)。您似乎想更新用户位置的字幕:

    - (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
    {
        [self updateSubtitleForUserLocation:userLocation];
    }
    

    这将调用这个方法来执行反向地理编码并相应地更新字幕:

    - (void)updateSubtitleForUserLocation:(MKUserLocation *)userLocation
    {
        if ([self.geocoder isGeocoding])
            [self.geocoder cancelGeocode];
    
        [self.geocoder reverseGeocodeLocation:userLocation.location completionHandler:^(NSArray *placemarks, NSError *error) {
            MKPlacemark *placemark = placemarks[0];
            userLocation.subtitle = placemark.name;
        }];
    }
    

    显然,您需要一个类属性:

    @property (nonatomic, strong) CLGeocoder *geocoder;
    

    你需要实例化它,例如viewDidLoad 可以:

    self.geocoder = [[CLGeocoder alloc] init];
    

    如果用户的位置变化超过 x 米,我可能会稍微改进一下,只执行反向地理编码(您不想执行太多的反向地理编码请求;您将杀死用户的电池),但希望这能说明这个想法。

    【讨论】:

    • 是的...我明白了...谢谢 ;)
    • 视情况而定。如果用户在地图上手动平移,您可以响应mapView:regionDidChangeAnimated: 执行此操作(尽管您会添加逻辑以检查以确保您尚未添加特定注释)。如果您允许userTrackingModeMKUserTrackingModeFollow,您可能会添加更多逻辑以确保您不会过于频繁地调用它。
    • @user2242550 查看修改后的答案。
    • @user2242550 是的,在 Apple 地图中调出方向。它涉及对代码进行轻微重构,以使用跟踪原始MKMapItem 的自定义注释,但是如果您将原始地图项保存在这样的注释中,openInMapsWithLaunchOptions 可以很容易地做到这一点。
    • 好的...谢谢,但下次我会注意你的建议
    猜你喜欢
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    • 2014-12-17
    • 1970-01-01
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多