Centrando MKMapView en el punto N-pixels debajo del pin
Desea centrar MKMapView en un punto N píxeles debajo de un pin dado (que puede o no ser visible en el MapRect actual ).
He estado tratando de resolver esto usando varias jugadas con -(CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(UIView *)view
sin éxito.
Alguien ha estado por este camino ( sin juego de palabras)?
7 answers
La técnica más fácil es simplemente desplazar el mapa hacia abajo, digamos un 40% desde donde estaría el coordinate
, aprovechando el span
del region
del MKMapView
. Si no necesita píxeles reales, pero solo necesita que se mueva hacia abajo para que el CLLocationCoordinate2D
en cuestión esté cerca de la parte superior del mapa (digamos un 10% lejos de la parte superior):
CLLocationCoordinate2D center = coordinate;
center.latitude -= self.mapView.region.span.latitudeDelta * 0.40;
[self.mapView setCenterCoordinate:center animated:YES];
Si desea tener en cuenta la rotación y el tono de la cámara, la técnica anterior puede no ser adecuada. En ese caso, usted podría:
-
Identificar el posición en la vista a la que desea cambiar la ubicación del usuario;
Convertir eso en un
CLLocation
;-
Calcule la distancia de la ubicación del usuario actual desde esa nueva ubicación deseada;
Mueva la cámara a esa distancia en la dirección 180° desde el rumbo actual de la cámara del mapa.
Por ejemplo, en Swift 3, algo como: {[19]]}
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
Donde CLLocationCoordinate2D
tiene lo siguiente 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
}
}
Así que, incluso con la cámara inclinada y en una dirección que no sea hacia el norte, esto mueve la ubicación del usuario (que está centrada, donde está el punto de mira inferior) hasta 150 píxeles (donde está el punto de mira superior), produciendo algo como:
Obviamente, debes ser consciente de las situaciones degeneradas (por ejemplo, estás a 1 km del polo sur y tratas de cambiar el mapa a 2 km de metros; estás usando un ángulo de cámara tan inclinado que la ubicación de la pantalla deseada está más allá del horizonte; etc.), pero para escenarios prácticos del mundo real, algo como lo anterior podría ser suficiente. Obviamente, si no dejas que el usuario cambie el tono de la cámara, la respuesta es aún más fácil.
Respuesta original: para mover la anotación n
píxeles
Si tienes un CLLocationCoordinate2D
, puedes convertirlo a un CGPoint
, moverlo x píxeles, y luego convertirlo de nuevo a 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];
}
Puede llamar a esto por:
[self moveCenterByOffset:CGPointMake(0, 100) from:coordinate];
Desafortunadamente, esto solo funciona si el coordinate
es visible antes de comenzar, por lo que es posible que tenga que ir a la coordenada original primero, y luego ajustar el centro.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-03-05 14:38:40
La única manera de hacer esto de manera confiable es usar lo siguiente:
- (void)setVisibleMapRect:(MKMapRect)mapRect edgePadding:(UIEdgeInsets)insets animated:(BOOL)animate
Para hacer eso dada una región del mapa en la que desea centrar, tiene que convertir la región del mapa a un MKMapRect. Utilice el relleno de borde para el desplazamiento de píxeles, obviamente.
Ver aquí para eso: Convertir MKCoordinateRegion a MKMapRect
Comentario: Me parece bastante extraño que esa sea la única manera de hacerlo, dado que MKMapRect no es algo que normalmente se usa con un MKMapView-todos los los métodos de conversión son para MKMapRegion. Pero, ok, al menos funciona. Probado en mi propio proyecto.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:27
Para 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)
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-14 05:19:08
Una solución sencilla es hacer que el marco de la vista de mapa sea más grande que el área visible. Luego coloque su pin en el centro de la vista de mapa y oculte todas las áreas no deseadas detrás de otra vista o fuera de los límites de la pantalla.
Déjame explicarte. Si miro su captura de pantalla, haga lo siguiente:
La distancia entre el pin y la parte inferior es de 353 píxeles. Así que haz que tus vistas de mapa tengan el doble de altura: 706 píxeles. La captura de pantalla tiene una altura de 411 píxeles. Coloque su marco en un origen de 706px-411px = -293 pixel. Ahora centre su vista de mapa en la coordenada del pin y listo.
Actualización 4-Marzo-2014:
He creado una pequeña aplicación de ejemplo con Xcode 5.0.2 para demostrar esto: http://cl.ly/0e2v0u3G2q1d
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-04 14:29:01
SWIFT 3 ACTUALIZADO
Se ha actualizado la función con 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)
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-28 08:27:28
Después de leer este anuncio de hilo jugando, especialmente con el zoom en la anotación, terminé con los siguientes procedimientos:
** Centrado en la anotación:**
- (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 sobre la anotación: * *
- (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
y -(CGFloat) sizeOfTop
ambos devuelven la altura de los paneles que cubren la vista del mapa desde las guías de diseño
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-20 16:59:01
Mira este método en MKMapView
:
- (void)setCenterCoordinate:(CLLocationCoordinate2D)coordinate animated:(BOOL)animated
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-03-14 22:27:23