kinamin
kinamin

Reputation: 127

crash on setUserTrackingMode with MKMapView when zoomed in

I have MKMapView with MKUserTrackingModeFollowWithHeading. But something changes the userTrackingMode to MKUserTrackingModeFollow or None, so I implemented,

- (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];
    }
}

Everything is fine but everytime I zoom in the map to most detail level, the app causes EXC_BAD_ACCESS in the line setUserTrackingMode:MKUserTrackingModeFollowWithHeading shown above.

What should I do to avoid crashing? I don't want to use MKUserTrackingBarButtonItem if possible.

The other part of the mapViewController is below.

- (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];
}

Any kind of advice appreciated. Thank you in advance.

I upload minimum sample code to github.

Upvotes: 3

Views: 2672

Answers (1)

Rob
Rob

Reputation: 437372

I might suggest trying to defer the setting of the tracking mode, e.g.:

- (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];
        }
    });
}

I might also suggest checking to make sure the mode isn't already what you want, eliminating a redundant 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];
        });
    }
}

You could also combine that with scrollEnabled (which should prevent the user from incidentally initiating a changing of the tracking mode).

Upvotes: 5

Related Questions