放大时使用 MKMapView 在 setUserTrackingMode 上崩溃

Posted

技术标签:

【中文标题】放大时使用 MKMapView 在 setUserTrackingMode 上崩溃【英文标题】:crash on setUserTrackingMode with MKMapView when zoomed in 【发布时间】:2013-05-17 20:42:06 【问题描述】:

我有 MKMapView 和 MKUserTrackingModeFollowWithHeading。 但是有些东西将 userTrackingMode 更改为 MKUserTrackingModeFollow 或 None, 所以我实现了,

- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated

    if ([CLLocationManager locationServicesEnabled]) 
        if ([CLLocationManager headingAvailable]) 
            [self.myMapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:NO];
        else
            [self.myMapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
        
    else
        [self.myMapView setUserTrackingMode:MKUserTrackingModeNone animated:NO];
    

一切都很好,但每次我将地图放大到最详细的级别时,应用程序都会在上面显示的 setUserTrackingMode:MKUserTrackingModeFollowWithHeading 行中导致 EXC_BAD_ACCESS。

我应该怎么做才能避免崩溃?如果可能,我不想使用 MKUserTrackingBarButtonItem。

mapViewController 的另一部分如下。

- (void)dealloc

    self.myMapView.delegate = nil;


- (void)viewWillDisappear:(BOOL)animated

    if ([CLLocationManager locationServicesEnabled]) 
        self.myMapView.showsUserLocation = NO;
        [_locManager stopUpdatingLocation];

        if ([CLLocationManager headingAvailable]) 
            [_locManager stopUpdatingHeading];
        
    

    [super viewWillDisappear:animated];


- (void)viewDidAppear:(BOOL)animated

    if ([CLLocationManager locationServicesEnabled]) 
        self.myMapView.showsUserLocation = YES;
        [_locManager startUpdatingLocation];

        if ([CLLocationManager headingAvailable]) 
            [self.myMapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:NO];
            [_locManager startUpdatingHeading];
        else
            [self.myMapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
        
    else
        [self.myMapView setUserTrackingMode:MKUserTrackingModeNone animated:NO];
    


- (void)viewWillAppear:(BOOL)animated

    [super viewDidAppear:animated];

    self.myMapView.delegate = self;
    [self.myMapView setFrame:self.view.frame];

    self.locManager = [CLLocationManager new];
    [self.locManager setDelegate:self];
    [self.locManager setDistanceFilter:kCLDistanceFilterNone];
    [self.locManager setDesiredAccuracy:kCLLocationAccuracyBest];
    [self.locManager setHeadingFilter:3];
    [self.locManager setHeadingOrientation:CLDeviceOrientationPortrait];

任何形式的建议表示赞赏。提前谢谢你。

我将最低示例代码上传到github。

【问题讨论】:

我认为这个崩溃(MKUserTrackingModeFollowWithHeading crash)在 SO 上很流行,你应该看看其他关于这个崩溃的 SO 帖子 我正在寻找 SO 但我找不到...谢谢。 不是强制重置跟踪模式,你不能把scrollEnabled设置为NO吗?这样用户可以缩放,但不能滚动,因此用户跟踪模式不应该因为用户交互而改变,不是吗?另外,如果这不是您当前所处的模式,您不应该只设置跟踪模式吗?如果didChangeUserTrackingMode设置成你想要的模式,就不用再设置用户跟踪模式了。 我尝试了scrollEnabled = NO,但应用程序仍然崩溃。但是您在下面给出的答案似乎可以解决问题。谢谢。 【参考方案1】:

我可能会建议尝试推迟跟踪模式的设置,例如:

- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated

    dispatch_async(dispatch_get_main_queue(),^
        if ([CLLocationManager locationServicesEnabled]) 
            if ([CLLocationManager headingAvailable]) 
                [self.myMapView setUserTrackingMode:MKUserTrackingModeFollowWithHeading animated:NO];
            else
                [self.myMapView setUserTrackingMode:MKUserTrackingModeFollow animated:NO];
            
        else
            [self.myMapView setUserTrackingMode:MKUserTrackingModeNone animated:NO];
        
    );

我可能还建议检查以确保 mode 不是您想要的,消除多余的 setUserTrackingMode

- (void)mapView:(MKMapView *)mapView didChangeUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated

    MKUserTrackingMode newMode = MKUserTrackingModeNone;

    if ([CLLocationManager locationServicesEnabled]) 
        if ([CLLocationManager headingAvailable]) 
            newMode = MKUserTrackingModeFollowWithHeading;
        else
            newMode = MKUserTrackingModeFollow;
        
    

    if (mode != newMode)
    
        dispatch_async(dispatch_get_main_queue(), ^
            [self.myMapView setUserTrackingMode:newMode animated:YES];
        );
    

您还可以将其与scrollEnabled 结合使用(这应该可以防止用户意外启动跟踪模式的更改)。

【讨论】:

这完美解决了问题!我一起使用这个和scrollEnabled = NO。我永远感激不尽! @kinamin 仅供参考,我注意到当你一直放大时,它会将跟踪模式设置为MKUserTrackingModeFollow,即使你只是将它设置为MKUserTrackingModeFollowWithHeading,你最终会得到一个didChangeUserTrackingMode 调用的循环,有时会导致崩溃。我在regionWillChangeAnimatedregionDidChangeAnimated 中设置了一个标志,它似乎解决了这个问题,但它仍然不太正确。最重要的是,如果您有问题,请告诉我,我可能会有更多建议。 您的意思是在regionWillChangeAnimatedregionDidChangeAnimated 方法中检测到缩放时,trackingMode 也必须更改为 MKUserTrackingModeFollowWithHeading?还是仅在 regionWillChangeAnimated / regionDidChangeAnimated 而不在 didChangeUserTrackingMode 中?当然,缩放时它似乎很慢。我认为可能发生了didChangeUserTrackingMode 循环。 我尝试在regionWillAnimatedregionDidAnimated(而不是didChangeUserTrackingMode)中试验setUserTrackingMode。放大到最大,userTrackingMode 固定为 MKUserTrackingModeFollow。所以我认为didChangeUserTrackingMode中需要它... @kinamin 不,我建议您在didChangeUserTrackingMode 中放置一个 NSLog,当您几乎完全放大时,您会看到它在用户更改缩放模式时被反复调用。 【参考方案2】:

斯威夫特 3

func mapView(_ mapView: MKMapView, didChange mode: MKUserTrackingMode, animated: Bool)         
        var newMode: MKUserTrackingMode = .none
        if ( CLLocationManager.locationServicesEnabled() ) 
            if ( CLLocationManager.headingAvailable() ) 
                newMode = .followWithHeading
            
            else 
                newMode = .follow
            
        

        if (mode != newMode)
        
            DispatchQueue.main.async 
                mapView.setUserTrackingMode(newMode, animated: true)
            
        

【讨论】:

以上是关于放大时使用 MKMapView 在 setUserTrackingMode 上崩溃的主要内容,如果未能解决你的问题,请参考以下文章

防止覆盖在缩放时消失 - MKMapView & MKOverlay

在启动时可靠地选择 MKMapView 中的注释(在 ios 6 和 ios 5.1 中)?

将 MKMapView 缩放到 MKAnnotationView 的框架

如何使 MKMapView 从顶部进入

防止 MKMapView 不断地重新缩放和重新居中到用户位置

加载 MKMapView 时崩溃