Centrage de MKMapView sur spot n-pixels en dessous de la pin

veut centrer MKMapView sur un point n-pixels au-dessous d'une pin donnée ( qui peut ou ne peut pas être visible dans la MapRect actuelle ).

j'ai essayé de résoudre cela en utilisant divers jeux avec -(CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view sans succès.

Quelqu'un est passé par là ( sans jeu de mots )?

enter image description here

33
demandé sur eric 2013-03-15 02:06:09

7 réponses

la technique la plus simple est de simplement décaler la carte vers le bas, disons 40% d'où le coordinate serait, en profitant du span du region du MKMapView . Si vous n'avez pas besoin de pixels réels, mais juste besoin de se déplacer vers le bas de sorte que le CLLocationCoordinate2D en question Est près du haut de la carte (disons 10% loin du haut):

CLLocationCoordinate2D center = coordinate;
center.latitude -= self.mapView.region.span.latitudeDelta * 0.40;
[self.mapView setCenterCoordinate:center animated:YES];

si vous voulez tenir compte de la rotation et du Pas de la caméra, le ci-dessus la technique peut ne pas être suffisante. Dans ce cas, vous pouvez:

  • identifiez la position dans la vue vers laquelle vous voulez déplacer l'emplacement de l'utilisateur;

  • Convertissez cela en CLLocation ;

  • calculer la distance entre l'emplacement actuel de l'utilisateur et le nouvel emplacement désiré;

  • déplacer la caméra par cette distance dans la direction 180° du cap actuel de la caméra de la carte.

E. G. dans Swift 3, quelque chose comme:

var point = mapView.convert(mapView.centerCoordinate, toPointTo: view)
point.y -= offset
let coordinate = mapView.convert(point, toCoordinateFrom: view)
let offsetLocation = coordinate.location

let distance = mapView.centerCoordinate.location.distance(from: offsetLocation) / 1000.0

let camera = mapView.camera
let adjustedCenter = mapView.centerCoordinate.adjust(by: distance, at: camera.heading - 180.0)
camera.centerCoordinate = adjustedCenter

CLLocationCoordinate2D a le suivant extension :

extension CLLocationCoordinate2D {
    var location: CLLocation {
        return CLLocation(latitude: latitude, longitude: longitude)
    }

    private func radians(from degrees: CLLocationDegrees) -> Double {
        return degrees * .pi / 180.0
    }

    private func degrees(from radians: Double) -> CLLocationDegrees {
        return radians * 180.0 / .pi
    }

    func adjust(by distance: CLLocationDistance, at bearing: CLLocationDegrees) -> CLLocationCoordinate2D {
        let distanceRadians = distance / 6_371.0   // 6,371 = Earth's radius in km
        let bearingRadians = radians(from: bearing)
        let fromLatRadians = radians(from: latitude)
        let fromLonRadians = radians(from: longitude)

        let toLatRadians = asin( sin(fromLatRadians) * cos(distanceRadians)
            + cos(fromLatRadians) * sin(distanceRadians) * cos(bearingRadians) )

        var toLonRadians = fromLonRadians + atan2(sin(bearingRadians)
            * sin(distanceRadians) * cos(fromLatRadians), cos(distanceRadians)
                - sin(fromLatRadians) * sin(toLatRadians))

        // adjust toLonRadians to be in the range -180 to +180...
        toLonRadians = fmod((toLonRadians + 3.0 * .pi), (2.0 * .pi)) - .pi

        let result = CLLocationCoordinate2D(latitude: degrees(from: toLatRadians), longitude: degrees(from: toLonRadians))

        return result
    }
}

ainsi, même avec la caméra pointée et à un cap autre que plein nord, cela déplace l'emplacement de l'utilisateur (qui est centré, où la ligne inférieure est) jusqu'à 150 pixels (où la ligne supérieure crosshair est), donnant quelque chose comme:

enter image description here

évidemment, vous devriez être conscient des situations dégénérées (par exemple, vous êtes à 1 km du pôle Sud et vous essayez de déplacer la carte jusqu'à 2 km mètres; vous utilisez un angle de caméra incliné si loin que l'emplacement de l'écran désiré est au-delà de l'horizon; etc.), mais pour des scénarios pratiques et réels, quelque chose comme ce qui précède pourrait suffire. Évidemment, si vous ne laissez pas l'utilisateur changer le ton de la caméra, la réponse est encore plus facile.


réponse originale: pour déplacer l'annotation n pixels

Si vous avez un CLLocationCoordinate2D , vous pouvez convertir en un CGPoint , le déplacer de x pixels et puis le reconvertir en un CLLocationCoordinate2D :

- (void)moveCenterByOffset:(CGPoint)offset from:(CLLocationCoordinate2D)coordinate
{
    CGPoint point = [self.mapView convertCoordinate:coordinate toPointToView:self.mapView];
    point.x += offset.x;
    point.y += offset.y;
    CLLocationCoordinate2D center = [self.mapView convertPoint:point toCoordinateFromView:self.mapView];
    [self.mapView setCenterCoordinate:center animated:YES];
}

vous pouvez l'appeler par:

[self moveCenterByOffset:CGPointMake(0, 100) from:coordinate];

malheureusement, cela ne fonctionne que si le coordinate est visible avant de commencer, donc vous pourriez avoir à aller à la coordonnée originale d'abord, puis ajuster le centre.

76
répondu Rob 2017-03-05 14:38:40

la seule façon de le faire de façon fiable est d'utiliser ce qui suit:

- (void)setVisibleMapRect:(MKMapRect)mapRect edgePadding:(UIEdgeInsets)insets animated:(BOOL)animate

pour faire cela étant donné une région cartographique sur laquelle vous voulez vous concentrer, vous devez convertir la région cartographique en MKMapRect. Utilisez le rembourrage de bord pour le décalage du pixel, évidemment.

voir ici pour cela: convertir MKCoordinateRegion en mkmaprect

commentaire: je trouve assez étrange que ce soit la seule façon de le faire, étant donné que MKMapRect n'est pas quelque chose qu'on utilise normalement avec un MKMapView - toutes les méthodes de conversion sont pour MKMapRegion. Mais, ok, au moins ça marche. Testé dans mon propre projet.

7
répondu n13 2017-05-23 12:34:27

Pour Swift:

import MapKit

extension MKMapView {

func moveCenterByOffSet(offSet: CGPoint, coordinate: CLLocationCoordinate2D) {
    var point = self.convert(coordinate, toPointTo: self)

    point.x += offSet.x
    point.y += offSet.y

    let center = self.convert(point, toCoordinateFrom: self)
    self.setCenter(center, animated: true)
}

func centerCoordinateByOffSet(offSet: CGPoint) -> CLLocationCoordinate2D {
    var point = self.center

    point.x += offSet.x
    point.y += offSet.y

    return self.convert(point, toCoordinateFrom: self)
}
}
3
répondu Steve Stomp 2018-07-14 05:19:08

une solution facile est que vous faites le cadre de votre vue de carte plus grand que la zone visible. Ensuite, placez votre NIP au centre de la vue de carte et de cacher toutes les zones indésirables derrière une autre vue ou en dehors des limites de l'écran.

laissez-moi développer. Si je regarde votre capture d'écran, Faites ce qui suit:

la distance entre votre pin et le fond est de 353 pixels. Donc, faites de votre vue de carte le cadre deux fois la hauteur: 706 pixel. Vous capture d'écran a une hauteur de 411 pixels. Positionnez votre image à une origine de 706px-411px = -293 pixel. Maintenant centrez votre vue de carte à la coordonnée de la broche et vous êtes fait.

Mise À Jour 4-Mars-2014:

j'ai créé un petit exemple d'application avec Xcode 5.0.2 pour démontrer ceci: http://cl.ly/0e2v0u3G2q1d

2
répondu Klaas 2014-03-04 14:29:01

SWIFT 3 MISE À JOUR

mise à jour de la fonction avec Zoom

func zoomToPos() {

        let span = MKCoordinateSpan(latitudeDelta: 0.1, longitudeDelta:  0.1)

        // Create a new MKMapRegion with the new span, using the center we want.
        let coordinate = moveCenterByOffset(offset: CGPoint(x: 0, y: 100), coordinate: (officeDetail?.coordinate)!)
        let region = MKCoordinateRegion(center: coordinate, span: span)

        mapView.setRegion(region, animated: true)


    }

    func moveCenterByOffset (offset: CGPoint, coordinate: CLLocationCoordinate2D) -> CLLocationCoordinate2D {
        var point = self.mapView.convert(coordinate, toPointTo: self.mapView)
        point.x += offset.x
        point.y += offset.y
        return self.mapView.convert(point, toCoordinateFrom: self.mapView)
    }
1
répondu Mikel Sanchez 2016-09-28 08:27:28

après avoir lu ce fil publicitaire jouant autour, en particulier avec le zoom sur l'annotation, je me suis retrouvé avec les procédures suivantes:

* * centrage sur annotation:* *

- (void) centerOnSelection:(id<MKAnnotation>)annotation
{
    MKCoordinateRegion region = self.mapView.region;
    region.center = annotation.coordinate;

    CGFloat per = ([self sizeOfBottom] - [self sizeOfTop]) / (2 * self.mapView.frame.size.height);
    region.center.latitude -= self.mapView.region.span.latitudeDelta * per;

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

** Zoom sur l'annotation:**

- (void) zoomAndCenterOnSelection:(id<MKAnnotation>)annotation
{
    DLog(@"zoomAndCenterOnSelection");

    MKCoordinateRegion region = self.mapView.region;
    MKCoordinateSpan span = MKCoordinateSpanMake(0.005, 0.005);

    region.center = annotation.coordinate;

    CGFloat per = ([self sizeOfBottom] - [self sizeOfTop]) / (2 * self.mapView.frame.size.height);
    region.center.latitude -= self.mapView.region.span.latitudeDelta * span.latitudeDelta / region.span.latitudeDelta * per;

    region.span = span;

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

-(CGFloat) sizeOfBottom et -(CGFloat) sizeOfTop tous deux hauteur de retour des panneaux couvrant le mapview à partir des guides de mise en page

-1
répondu altagir 2015-11-20 16:59:01

Regardez cette méthode sur MKMapView :

- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated
-6
répondu Jack Freeman 2013-03-14 22:27:23