2010-11-07 24 views
9

Estoy tratando de agregar una imagen personalizada a MKMapView como MKOverlayView - Necesito restringir a los usuarios para que puedan desplazarse fuera de los límites de la superposición. ¿Hay alguna función existente para hacer esto? ¿O alguna otra sugerencia?Restringir el desplazamiento de MKMapView

Gracias, Matt

Respuesta

18

Si sólo desea congelar la vista del mapa en la superposición, podría configurar región de la vista del mapa a los límites de la superposición y establecer scrollEnabled y zoomEnabled a NO.

Pero eso no permitirá al usuario desplazarse o acercarse dentro de los límites de la superposición.

No hay formas integradas de restringir la vista del mapa a los límites de la superposición, por lo que tendría que hacerlo manualmente. Primero, asegúrese de que su objeto MKOverlay implemente la propiedad boundingMapRect. Esto se puede usar en el método de delegado regionDidChangeAnimated para ajustar manualmente la vista según sea necesario.

Aquí hay un ejemplo de cómo se podría hacer esto.
El siguiente código debe estar en la clase que tiene el MKMapView.
Asegúrate de que la vista del mapa esté inicialmente configurada en una región donde la superposición esté visible.

//add two ivars to the .h... 
MKMapRect lastGoodMapRect; 
BOOL manuallyChangingMapRect; 

//in the .m... 
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated 
{ 
    if (manuallyChangingMapRect) 
     return;  
    lastGoodMapRect = mapView.visibleMapRect; 
} 

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated 
{ 
    if (manuallyChangingMapRect) //prevents possible infinite recursion when we call setVisibleMapRect below 
     return;  

    // "theOverlay" below is a reference to your MKOverlay object. 
    // It could be an ivar or obtained from mapView.overlays array. 

    BOOL mapContainsOverlay = MKMapRectContainsRect(mapView.visibleMapRect, theOverlay.boundingMapRect); 

    if (mapContainsOverlay) 
    { 
     // The overlay is entirely inside the map view but adjust if user is zoomed out too much... 
     double widthRatio = theOverlay.boundingMapRect.size.width/mapView.visibleMapRect.size.width; 
     double heightRatio = theOverlay.boundingMapRect.size.height/mapView.visibleMapRect.size.height; 
     if ((widthRatio < 0.6) || (heightRatio < 0.6)) //adjust ratios as needed 
     { 
      manuallyChangingMapRect = YES; 
      [mapView setVisibleMapRect:theOverlay.boundingMapRect animated:YES]; 
      manuallyChangingMapRect = NO; 
     } 
    } 
    else 
     if (![theOverlay intersectsMapRect:mapView.visibleMapRect]) 
     { 
      // Overlay is no longer visible in the map view. 
      // Reset to last "good" map rect... 
      [mapView setVisibleMapRect:lastGoodMapRect animated:YES]; 
     } 
} 

He intentado esto con la incorporada en el MKCircle superposición y parece funcionar bien.


EDIT:

No funciona bien el 95% de las veces, sin embargo, he confirmado a través de algunas pruebas de que podría oscilar entre dos ubicaciones, a continuación, entrar en un bucle infinito. Por lo tanto, he editado un poco, creo que esto debería resolver el problema:

// You can safely delete this method: 
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated { 

} 

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated { 
    // prevents possible infinite recursion when we call setVisibleMapRect below 
    if (manuallyChangingMapRect) { 
     return; 
    } 

    // "theOverlay" below is a reference to your MKOverlay object. 
    // It could be an ivar or obtained from mapView.overlays array. 

    BOOL mapContainsOverlay = MKMapRectContainsRect(mapView.visibleMapRect, theOverlay.boundingMapRect); 

    if (mapContainsOverlay) { 
     // The overlay is entirely inside the map view but adjust if user is zoomed out too much... 
     double widthRatio = theOverlay.boundingMapRect.size.width/mapView.visibleMapRect.size.width; 
     double heightRatio = theOverlay.boundingMapRect.size.height/mapView.visibleMapRect.size.height; 
     // adjust ratios as needed 
     if ((widthRatio < 0.6) || (heightRatio < 0.6)) { 
      manuallyChangingMapRect = YES; 
      [mapView setVisibleMapRect:theOverlay.boundingMapRect animated:YES]; 
      manuallyChangingMapRect = NO; 
     } 
    } else if (![theOverlay intersectsMapRect:mapView.visibleMapRect]) { 
     // Overlay is no longer visible in the map view. 
     // Reset to last "good" map rect... 
     manuallyChangingMapRect = YES; 
     [mapView setVisibleMapRect:lastGoodMapRect animated:YES]; 
     manuallyChangingMapRect = NO; 
    } else { 
     lastGoodMapRect = mapView.visibleMapRect; 
    } 
} 

Y en caso de que alguien está buscando una solución rápida MKOverlay, aquí es una:

- (void)viewDidLoad { 
    [super viewDidLoad]; 

    MKCircle* circleOverlay = [MKCircle circleWithMapRect:istanbulRect]; 
    [_mapView addOverlay:circleOverlay]; 

    theOverlay = circleOverlay; 
} 

- (MKOverlayView *)mapView:(MKMapView *)mapView viewForOverlay:(id<MKOverlay>)overlay { 
    MKCircleView* circleOverlay = [[MKCircleView alloc] initWithCircle:overlay]; 
    [circleOverlay setStrokeColor:[UIColor mainColor]]; 
    [circleOverlay setLineWidth:4.f]; 

    return circleOverlay; 
} 
+0

¡Gracias! Eso hizo el truco. – mag725

+0

funcionó bastante bien. no maneja los casos en los que desplaza (desplaza, desplaza, desplaza, lo que sea) la vista fuera de sus límites, sin embargo. – horseshoe7

+0

@ horseshoe7, ¿vuelve la vista a la superposición después de finalizar el gesto? Además, el mapa debe comenzar dentro de la superposición para que funcione lo anterior.Si necesita restringir _while_ el usuario está moviendo el mapa, _might_ podrá hacerlo usando un UIPanGestureRecognizer y verificando la nueva región del mapa en el controlador de gestos. – Anna

4

En mi caso, necesitaba restringir los límites a la superposición de mosaico que tiene una coordenadas upperleft/lowerRight. Código anterior todavía funciona bien, pero theOverlay.boundingMapRect sustituido por MKMapRect paddedBoundingMapRect

- (void)mapView:(MKMapView *)_mapView regionDidChangeAnimated:(BOOL)animated 
{ 
if (manuallyChangingMapRect) //prevents possible infinite recursion when we call setVisibleMapRect below 
    return;  

[self updateDynamicPaddedBounds]; 

MKMapPoint pt = MKMapPointForCoordinate(mapView.centerCoordinate); 

BOOL mapInsidePaddedBoundingRect = MKMapRectContainsPoint(paddedBoundingMapRect,pt); 

if (!mapInsidePaddedBoundingRect) 
{ 
    // Overlay is no longer visible in the map view. 
    // Reset to last "good" map rect... 

    manuallyChangingMapRect = YES; 
    [mapView setVisibleMapRect:lastGoodMapRect animated:YES]; 
    manuallyChangingMapRect = NO; 


} 


-(void)updateDynamicPaddedBounds{ 

ENTER_METHOD; 

CLLocationCoordinate2D northWestPoint= CLLocationCoordinate2DMake(-33.841171,151.237318); 
CLLocationCoordinate2D southEastPoint= CLLocationCoordinate2DMake(-33.846127, 151.245058); 



MKMapPoint upperLeft = MKMapPointForCoordinate(northWestPoint); 
MKMapPoint lowerRight = MKMapPointForCoordinate(southEastPoint); 
double width = lowerRight.x - upperLeft.x; 
double height = lowerRight.y - upperLeft.y; 


MKMapRect mRect = mapView.visibleMapRect; 
MKMapPoint eastMapPoint = MKMapPointMake(MKMapRectGetMinX(mRect), MKMapRectGetMidY(mRect)); 
MKMapPoint westMapPoint = MKMapPointMake(MKMapRectGetMaxX(mRect), MKMapRectGetMidY(mRect)); 
MKMapPoint northMapPoint = MKMapPointMake(MKMapRectGetMidX(mRect), MKMapRectGetMaxY(mRect)); 
MKMapPoint southMapPoint = MKMapPointMake(MKMapRectGetMidX(mRect), MKMapRectGetMinY(mRect)); 

double xMidDist = abs(eastMapPoint.x - westMapPoint.x)/2; 
double yMidDist = abs(northMapPoint.y - southMapPoint.y)/2; 


upperLeft.x = upperLeft.x + xMidDist; 
upperLeft.y = upperLeft.y + yMidDist; 


double paddedWidth = width - (xMidDist*2); 
double paddedHeight = height - (yMidDist*2); 

paddedBoundingMapRect= MKMapRectMake(upperLeft.x, upperLeft.y, paddedWidth, paddedHeight); 

}

0

(https://stackoverflow.com/a/4126011/3191130) La solución de Anna en Swift 3.0, que añade a una extensión:

extension HomeViewController: MKMapViewDelegate { 
func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) { 
    if manuallyChangingMapRect { 
     return 
    } 
    guard let overlay = self.mapOverlay else { 
     print("Overlay is nil") 
     return 
    } 
    guard let lastMapRect = self.lastGoodMapRect else { 
     print("LastGoodMapRect is nil") 
     return 
    } 

    let mapContainsOverlay = MKMapRectContainsRect(mapView.visibleMapRect, overlay.boundingMapRect) 
    if mapContainsOverlay { 
     let widthRatio: Double = overlay.boundingMapRect.size.width/mapView.visibleMapRect.size.width 
     let heightRatio: Double = overlay.boundingMapRect.size.height/mapView.visibleMapRect.size.height 
     // adjust ratios as needed 
     if (widthRatio < 0.9) || (heightRatio < 0.9) { 
      manuallyChangingMapRect = true 
      mapView.setVisibleMapRect(overlay.boundingMapRect, animated: true) 
      manuallyChangingMapRect = false 
     } 
    } else if !overlay.intersects(mapView.visibleMapRect) { 
      // Overlay is no longer visible in the map view. 
      // Reset to last "good" map rect... 
      manuallyChangingMapRect = true 
      mapView.setVisibleMapRect(lastMapRect, animated: true) 
      manuallyChangingMapRect = false 
     } 
     else { 
      lastGoodMapRect = mapView.visibleMapRect 
    } 
} 
} 

Para configurar el mapa usa esto:

override func viewDidLoad() { 
    super.viewDidLoad() 
    setupMap() 
} 

func setupMap() { 
    mapView.delegate = self 
    let radius:CLLocationDistance = 1000000 
    mapOverlay = MKCircle(center: getCenterCoord(), radius: radius) 
    if let mapOverlay = mapOverlay { 
     mapView.add(mapOverlay) 
    } 
    mapView.setRegion(MKCoordinateRegionMake(getCenterCoord(), getSpan()), animated: true) 
    lastGoodMapRect = mapView.visibleMapRect 
} 

func getCenterCoord() -> CLLocationCoordinate2D { 
    return CLLocationCoordinate2DMake(LAT, LON) 
} 
func getSpan() -> MKCoordinateSpan { 
    return MKCoordinateSpanMake(10, 10) 
}