2010-11-07 17 views
9

Sto tentando di aggiungere un'immagine personalizzata a MKMapView come MKOverlayView - È necessario limitare gli utenti dall'essere in grado di scorrere all'esterno dei limiti dell'overlay. Ci sono delle funzioni esistenti per farlo? O qualche altro suggerimento?Limita scorrimento MKMapView

Grazie, Matt

risposta

18

Se si desidera solo per bloccare la visualizzazione della mappa in overlay, è possibile impostare regione della vista mappa per limiti della sovrapposizione e impostare scrollEnabled e zoomEnabled-NO.

Ma questo non consente all'utente di scorrere o ingrandire i limiti della sovrapposizione.

Non esistono modi incorporati per limitare la visualizzazione della mappa ai limiti dell'overlay, quindi è necessario farlo manualmente. Innanzitutto, assicurati che l'oggetto MKOverlay implementa la proprietà boundingMapRect. Può quindi essere utilizzato nel metodo delegato regionDidChangeAnimated per regolare manualmente la vista in base alle esigenze.

Ecco un esempio di come questo potrebbe essere fatto.
Il codice di seguito dovrebbe essere nella classe che ha il MKMapView.
Assicurarsi che la vista mappa sia inizialmente impostata su una regione in cui l'overlay è visibile.

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

Ho provato questo con il built-in MKCircle sovrapposizione e sembra funzionare bene.


EDIT:

Esso funziona bene 95% del tempo, tuttavia, Ho confermato attraverso alcuni test che potrebbe oscillare tra due posizioni, quindi immettere un ciclo infinito. Così, ho modificato un po ', credo che questo dovrebbe risolvere il 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; 
    } 
} 

E solo nel caso in cui qualcuno è alla ricerca di una soluzione rapida MKOverlay, qui è uno:

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

Grazie! Questo ha fatto il trucco. – mag725

+0

ha funzionato abbastanza bene. non gestisce i casi in cui si effettua una panoramica (scorrimento, scorrimento o altro) della vista al di fuori dei limiti. – horseshoe7

+0

@ horseshoe7, la vista ritorna nell'overlay dopo aver terminato il gesto? Inoltre, la mappa deve iniziare all'interno della sovrapposizione affinché funzioni sopra.Se è necessario limitare _while_ l'utente sta spostando la mappa, è possibile essere in grado di farlo utilizzando un UIPanGestureRecognizer e controllando la nuova area della mappa nel gestore di gesti. – Anna

4

In Nel mio caso, avevo bisogno di limitare i limiti alla sovrapposizione piastrellata che ha le coordinate upperleft/lowerRight. Codice di cui sopra ancora funziona bene, ma sostituito theOverlay.boundingMapRect per 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 soluzione di Anna in Swift 3.0, ho aggiunto a un interno:

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

Per impostare la mappa usa questo:

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