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 )?
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
où 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:
é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.
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.
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)
}
}
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
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)
}
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
Regardez cette méthode sur MKMapView
:
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated