Wie zoome ich eine MKMapView ohne CLLocationManager auf den aktuellen Standort des Benutzers?

70

Mit der MKMapViewOption "Aktuellen Standort des Benutzers anzeigen" wird automatisch ein Standort des Benutzers auf dem angezeigt map.

Ich möchte diesen Ort verschieben und zoomen, wenn er gefunden wird (und wenn er sich ändert).

Das Problem ist, dass es anscheinend keine Methode gibt, die aufgerufen wird, wenn der Benutzerstandort auf dem aktualisiert mapwird zoom/scroll. Daher kann ich den entsprechenden Code nirgends einfügen .

Gibt es eine Möglichkeit, benachrichtigt zu werden, wenn ein MKMapViewBenutzer den Standort des Benutzers erhalten (oder aktualisiert) hat, damit ich ihn verschieben / zoomen kann? Wenn ich meine eigenen verwende, entsprechen CLLocationManagerdie Aktualisierungen, die ich erhalte, nicht den Aktualisierungen der Benutzermarkierung auf der Karte. Daher sieht es albern aus, wenn sich meine Karte Sekunden vor dem Erscheinen des blauen Stifts bewegt und zoomt.

Das fühlt sich wie eine grundlegende Funktionalität an, aber ich habe Wochen damit verbracht, nach einer Lösung zu suchen und nichts in der Nähe gefunden.

Danny Tuppeny
quelle
Ich habe einen CLLocationManager hinzugefügt, um dies manuell zu tun, aber er wird nicht einmal gleichzeitig ausgelöst, wenn MapView den Benutzerstandort zeichnet, sodass er naff aussieht. Ich verstehe nicht, warum dies so schwierig sein würde
Danny Tuppeny

Antworten:

106

Sie müssen sich für KVO-Benachrichtigungen über das userLocation.locationEigentum von registrieren MKMapView.

viewDidLoad:Fügen Sie dazu diesen Code in Ihren ViewController oder an eine beliebige Stelle an der Stelle ein, an der Ihre Kartenansicht initialisiert wird.

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

Implementieren Sie dann diese Methode, um KVO-Benachrichtigungen zu erhalten

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

Dieser Code funktioniert gut für mich.
BTW, selfist mein Viewcontroller in diesem Zusammenhang.

ddnv
quelle
1
Ich habe KVO noch nie benutzt, musste also Google. Sieht so aus, als würde das genau das tun, was ich brauche - danke!
Danny Tuppeny
6
In diesem Code ist ein Problem aufgetreten. Dies funktioniert nur, wenn sich der Benutzerstandort bereits irgendwo auf der Karte befindet. [self.mapView isUserLocationVisible] ist nur wahr, wenn sich der Benutzerstandort innerhalb des aktuellen Kartenbereichs befindet. Stattdessen sollte die Zeile lauten: self.mapView.showsUserLocation
Felix Lamouroux
@ Felix, das scheint nicht der Fall für mich zu sein
D-Nice
12
Für diejenigen, die sich nicht sicher sind, wie sie implementieren sollen: [self moveOrZoomOrAnythingElse]; Fügen Sie einfach die Kategorie unter diesem Link hinzu: troybrant.net/blog/2010/01/set-the-zoom-level-of-an-mkmapview zu Ihrem Code und ersetzen Sie dann die Zeile: [self moveOrZoomOrAnythingElse]; mit [self.mapView setCenterCoordinate: self.mapView.userLocation.location.coordinate zoomLevel: 14 animiert: YES]; Es wirkt wie ein Zauber.
Vibhor Goyal
Vielen Dank für die großartige Antwort ... Übrigens, diejenigen, die Schwierigkeiten haben, es zum Laufen zu bringen, müssen "mapView.showsUserLocation = YES;" Andernfalls funktioniert dies nicht.
Prasad De Zoysa
52

Dies ist eine Kombination aus ddnv und Dustins Antwort, die für mich funktioniert hat:

mapView ist der Name des MKMapView * mapView;

Beachten Sie beim Hinzufügen dieser Zeile in viewDidLoad, dass möglicherweise mehr Zeilen in der Last enthalten sind. Dies ist nur vereinfacht.

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

Erstellen Sie dann die tatsächliche Auflistungsmethode, mit der die Karte an den aktuellen Speicherort verschoben wird:

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

Vergessen Sie nicht, die Zuordnung ordnungsgemäß aufzuheben und die Registrierung des Beobachters aufzuheben:

- (void)dealloc 
{
    [self.mapView.userLocation removeObserver:self forKeyPath:@"location"];
    [self.mapView removeFromSuperview]; // release crashes app
    self.mapView = nil;
    [super dealloc];
}
MrHus
quelle
Nicht korrekt - Sie können entweder die Kombination init / dealloc oder viewDidLoad / viewDidUnload ausführen. viewDidLoad kann aufgerufen werden, bevor der Dealloc mehrmals auftritt. Wenn dies zum ersten Mal geschieht, stürzt die App ab.
Joshis
46

Seit iOS 5.0 hat Apple MKMapView um eine neue Methode erweitert. Diese Methode macht genau das, was Sie wollen und mehr.

Schauen Sie sich Folgendes an: https://developer.apple.com/documentation/mapkit/mkmapview

- (void)setUserTrackingMode:(MKUserTrackingMode)mode animated:(BOOL)animated;
Thijsai
quelle
6
Schöner Fang. Das ist so viel einfacher.
Chris Knadler
Perfekt! Hat mir eine Menge Zeit gespart. Vielen Dank!!
tg2
1
Dies sollte die akzeptierte Antwort ab iOS 5 sein. Eine Codezeile:[self.mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
Scott Heaberlin
13

Sie können überwachen, wann der MKMapViewBenutzerstandort auf der Karte aktualisiert wird, indem Sie das MKMapViewDelegateProtokoll implementieren . Einfach umsetzen:

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

Dieser Rückruf sollte perfekt mit dem übereinstimmen, was auf der Karte angezeigt wird.

Yonel
quelle
6

Versuche dies:

[mapView setUserTrackingMode:MKUserTrackingModeFollow animated:YES];
Kenan Karakecili
quelle
1

Kein Problem ... Fügen Sie in der viewDidLoad-Methode Ihrer UIViewController-Unterklasse, in der MKMapView vorhanden ist, Folgendes hinzu (vorausgesetzt, Ihre MKMapView heißt 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
Dustin Pfannenstiel
quelle
3
Ich habe so ziemlich alles versucht, aber das Problem schien zu sein, dass dieser Code ausgeführt wird, bevor der Standort des Benutzers bestimmt wurde.
Danny Tuppeny