2010-11-07 15 views
9

Próbuję dodać niestandardowy obraz do MKMapView jako MKOverlayView - Muszę uniemożliwić użytkownikom przewijanie poza obrębem nakładki. Czy istnieją jakieś funkcje do tego? Lub jakieś inne sugestie?Ograniczanie przewijania MKMapView

Dzięki, Matt

Odpowiedz

18

Jeśli chcesz po prostu zamrozić widok mapy w nakładce, można ustawić obszar widoku mapy do granic nakładkę i ustaw scrollEnabled i zoomEnabled do NO.

Ale to nie pozwoli użytkownikowi przewinąć ani powiększyć wewnątrz granic nałożenia.

Nie ma wbudowanych sposobów ograniczenia widoku mapy do granic nakładki, więc trzeba to zrobić ręcznie. Najpierw upewnij się, że obiekt MKOverlay implementuje właściwość boundingMapRect. Następnie można go użyć w metodzie delegata regionDidChangeAnimated, aby ręcznie dostosować widok w razie potrzeby.

Oto przykład tego, jak można to zrobić.
Poniższy kod powinien należeć do klasy o numerze MKMapView.
Upewnij się, że widok mapy jest początkowo ustawiony na region, w którym nakładka jest widoczna.

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

Próbowałem to z wbudowaną MKCircle nakładki i wydaje się działać dobrze.


Edycja:

to działa dobrze w 95% przypadków, jednakże, że potwierdzono przez pewne testy, które mogą oscylować dwóch miejscach, a następnie wejść w nieskończoną pętlę. Więc edytowany go trochę, myślę, że to powinno rozwiązać problem:

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

i tylko w przypadku, gdy ktoś patrzy na szybki MKOverlay rozwiązanie, tutaj jest jeden:

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

Dziękujemy! To wystarczyło. – mag725

+0

działa całkiem dobrze. nie obsługuje przypadków, w których przesuwasz (przesuwaj, przewijaj, cokolwiek) widok poza swoimi granicami. – horseshoe7

+0

@ horseshoe7, czy widok wskoczy z powrotem do nakładki po zakończeniu gestu? Ponadto mapa musi wystartować wewnątrz nakładki, aby powyższe działało.Jeśli musisz ograniczyć czas, gdy użytkownik przesunie mapę, możesz to zrobić za pomocą narzędzia UIPanGestureRecognizer i sprawdzić nowy obszar mapy w procedurze obsługi gestów. – Anna

4

W w moim przypadku, musiałem ograniczyć granice do nakładki z płytek, która ma współrzędne górnej/dolnej prędkości. Powyższy kod nadal działa dobrze, ale podstawiony theOverlay.boundingMapRect dla 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) rozwiązanie Anny w Swift 3.0, dodałem do rozszerzenia:

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

Aby skonfigurować mapa używa tego:

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