如何在不使用CLLocationManager的情况下将MKMapView放大到用户当前位置?

70

MKMapView提供了一个选项叫做“显示用户当前位置”,它会自动在地图上展示用户的位置。

当发现用户的位置(或位置改变)时,我希望将地图移动并缩放到该位置。

问题是,似乎没有任何方法可以在map更新用户位置时调用,所以我无处放置代码来进行缩放/滚动操作。

有没有一种方法可以在MKMapView获取(或更新)了用户位置后通知我,这样我就可以将其移动/缩放到这个位置?如果我使用自己的CLLocationManager获取位置更新,它们和地图上用户标记的更新时间不对应,因此在蓝色标记出现前,我的地图移动和缩放看起来很傻。

这似乎是基本功能,但我花了几周时间寻找解决方案,却没有找到类似的内容。


我已经手动添加了一个CLLocationManager来实现这个功能,但它甚至不能与MapView同时绘制用户位置,所以看起来很糟糕。我不明白为什么这会如此困难。 - Danny Tuppeny
6个回答

106

您需要注册MKMapViewuserLocation.location属性的KVO通知。

为此,请在您的ViewController的viewDidLoad:中或在初始化地图视图的任何地方放置此代码。

[self.mapView.userLocation addObserver:self  
        forKeyPath:@"location"  
           options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld)  
           context:NULL];

然后实现这个方法来接收 KVO 通知

- (void)observeValueForKeyPath:(NSString *)keyPath  
                      ofObject:(id)object  
                        change:(NSDictionary *)change  
                       context:(void *)context {  

    if ([self.mapView showsUserLocation]) {  
        [self moveOrZoomOrAnythingElse];
        // and of course you can use here old and new location values
    }
}

这段代码对我来说很好用。
顺便提一下,在这个上下文中,self是我的视图控制器。


1
我以前从未使用过KVO,所以不得不Google一下。看起来这正是我需要的 - 谢谢! - Danny Tuppeny
6
这段代码存在问题。它只在用户位置已经显示在地图上时才能正常工作。判断语句[self.mapView isUserLocationVisible]只有在用户位置在当前地图区域内才返回true。相反,修改成self.mapView.showsUserLocation可以解决该问题。 - Felix Lamouroux
@Felix,对我来说似乎不是这样。 - D-Nice
12
如果您对如何实现以下代码感到困惑:[self moveOrZoomOrAnythingElse];只需将此链接中的类别添加至您的代码:http://troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview/然后,用以下代码替换上面那一行:[self.mapView setCenterCoordinate:self.mapView.userLocation.location.coordinate zoomLevel:14 animated:YES];它能够完美地运作。 - Vibhor Goyal
感谢您提供出色的答案... 顺便说一句,那些努力让它工作的人,你必须设置“mapView.showsUserLocation = YES;”否则这将无法正常工作。 - Prasad De Zoysa
从iOS 5+开始,您应该使用MKUserTrackingMode。 - Senseful

52

这是ddnv和Dustin答案的结合,对我有效:

mapView是MKMapView *mapView的名称;

在viewDidLoad中添加此行,注意加载中可能有更多行。这只是简化版。

- (void) viewDidLoad
{
    [self.mapView.userLocation addObserver:self 
                                forKeyPath:@"location" 
                                   options:(NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld) 
                                   context:nil];
}

然后创建实际的列表方法,将地图移动到当前位置:

// Listen to change in the userLocation
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context 
{       
    MKCoordinateRegion region;
    region.center = self.mapView.userLocation.coordinate;  

    MKCoordinateSpan span; 
    span.latitudeDelta  = 1; // Change these values to change the zoom
    span.longitudeDelta = 1; 
    region.span = span;

    [self.mapView setRegion:region animated:YES];
}

不要忘记正确释放和取消观察者:

- (void)dealloc 
{
    [self.mapView.userLocation removeObserver:self forKeyPath:@"location"];
    [self.mapView removeFromSuperview]; // release crashes app
    self.mapView = nil;
    [super dealloc];
}

不正确-您可以选择使用init/dealloc或viewDidLoad/viewDidUnload组合。viewDidLoad可能会在多次dealloc发生之前被调用-第一次发生这种情况时,应用程序崩溃。 - joshis

47

6
不错的抓捕。这样做容易多了。 - Chris Knadler
太完美了!省了我大量的时间。谢谢!! - tg2
1
这应该是自iOS 5及以上版本的官方答案。只需一行代码:[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES]; - Scott Heaberlin

13

您可以通过实现MKMapViewDelegate协议来监视 MKMapView 在地图上更新用户位置的情况。只需实现以下内容:

- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation      {
    CLLocationAccuracy accuracy = userLocation.location.horizontalAccuracy;
    if (accuracy ......) {
    } 
}

这个回调函数应该完全与地图上显示的内容同步。


6

试试这个:

[mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];

1
没问题... 在你的UIViewController子类的viewDidLoad方法中,添加以下代码(假设你的MKMapView被命名为map):
CLLocation *location = [[[CLLocation alloc] initWithLatitude:map.centerCoordinate.latitude longitude:map.centerCoordinate.longitude] autorelease]; //Get your location and create a CLLocation
MKCoordinateRegion region; //create a region.  No this is not a pointer
region.center = location.coordinate;  // set the region center to your current location
MKCoordinateSpan span; // create a range of your view
span.latitudeDelta = BASE_RADIUS / 3;  // span dimensions.  I have BASE_RADIUS defined as 0.0144927536 which is equivalent to 1 mile
span.longitudeDelta = BASE_RADIUS / 3;  // span dimensions
region.span = span; // Set the region's span to the new span.
[map setRegion:region animated:YES]; // to set the map to the newly created region

3
我尽力尝试了这个,但问题似乎在于这段代码是在确定用户位置之前运行的。 - Danny Tuppeny

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