Prevent scrolling in a MKMapView, also when zooming

前端 未结 6 461
借酒劲吻你
借酒劲吻你 2020-12-03 03:09

The scrollEnabled seems to be breakable once the user starts pinching in a MKMapView.

You still can\'t scroll with one finger, but if y

相关标签:
6条回答
  • 2020-12-03 03:40

    I did not have a lot of luck with any of these answers. Doing my own pinch just conflicted too much. I was running into cases where the normal zoom would zoom farther in than I could do with my own pinch.

    Originally, I tried as the original poster to do something like:

    - (void) mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
        MKCoordinateRegion region = mapView.region;
        //...
        // adjust the region.center 
        //...
        mapView.region = region;
    }
    

    What I found was that that had no effect. I also discovered through NSLogs that this method will fire even when I set the region or centerCoordinate programmatically. Which led to the question: "Wouldn't the above, if it DID work go infinite?"

    So I'm conjecturing and hypothesizing now that while user zoom/scroll/rotate is happening, MapView somehow suppresses or ignores changes to the region. Something about the arbitration renders the programmatic adjustment impotent.

    If that's the problem, then maybe the key is to get the region adjustment outside of the regionDidChanged: notification. AND since any adjustment will trigger another notification, it is important that it be able to determine when not to adjust anymore. This led me to the following implementation (where subject is supplying the center coordinate that I want to stay in the middle):

    - (void) recenterMap {
        double latDiff = self.subject.coordinate.latitude self.mapView.centerCoordinate.latitude;
        double lonDiff = self.subject.coordinate.longitude - self.mapView.centerCoordinate.longitude;
        BOOL latIsDiff = ABS(latDiff) > 0.00001;
        BOOL lonIsDiff = ABS(lonDiff) > 0.00001;
        if (self.subject.isLocated && (lonIsDiff || latIsDiff)) {
            [self.mapView setCenterCoordinate: self.subject.coordinate animated: YES];
        }
    }
    
    - (void) mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
        if (self.isShowingMap) {
            if (self.isInEdit) {
                self.setLocationButton.hidden = NO;
                self.mapEditPrompt.hidden = YES;
            }
            else {
                if (self.subject.isLocated) { // dispatch outside so it will happen after the map view user events are done
                     dispatch_after(DISPATCH_TIME_NOW, dispatch_get_main_queue(), ^{
                        [self recenterMap];
                    });
                }
            }
        }
    }
    

    The delay where it slides it back can vary, but it really does work pretty well. And lets the map interaction remain Apple-esque while it's happening.

    0 讨论(0)
  • 2020-12-03 03:51

    Try implementing –mapView:regionWillChangeAnimated: or –mapView:regionDidChangeAnimated: in your map view's delegate so that the map is always centered on your preferred location.

    0 讨论(0)
  • 2020-12-03 03:51

    I tried this and it works.

    First create a property:

    var originalCenter: CLLocationCoordinate2D?
    

    Then in regionWillChangeAnimated, check if this event is caused by a UIPinchGestureRecognizer:

    func mapView(mapView: MKMapView, regionWillChangeAnimated animated: Bool) {
        let firstView = mapView.subviews.first
        if let recognizer = firstView?.gestureRecognizers?.filter({ $0.state == .Began || $0.state == .Ended }).first as? UIPinchGestureRecognizer {
            if recognizer.scale != 1.0 {
                originalCenter = mapView.region.center
            }
        }
    }
    

    Then in regionDidChangeAnimated, return to original region if a pinch gesture caused the region changing:

    func mapView(mapView: MKMapView, regionDidChangeAnimated animated: Bool) {
        if let center = originalCenter {
            mapView.setRegion(MKCoordinateRegion(center: center, span: mapView.region.span), animated: true)
            originalCenter = nil
            return
        }
    // your other code 
    }
    
    0 讨论(0)
  • 2020-12-03 03:53

    You can try to handle the pinch gestures yourself using a UIPinchGestureRecognizer:

    First set scrollEnabled and zoomEnabled to NO and create the gesture recognizer:

    UIPinchGestureRecognizer* recognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self
                                                                                     action:@selector(handlePinch:)];
    [self.mapView addGestureRecognizer:recognizer];
    

    In the recognizer handler adjust the MKCoordinateSpan according to the zoom scale:

    - (void)handlePinch:(UIPinchGestureRecognizer*)recognizer
    {
        static MKCoordinateRegion originalRegion;
        if (recognizer.state == UIGestureRecognizerStateBegan) {
            originalRegion = self.mapView.region;
        }    
    
        double latdelta = originalRegion.span.latitudeDelta / recognizer.scale;
        double londelta = originalRegion.span.longitudeDelta / recognizer.scale;
    
        // TODO: set these constants to appropriate values to set max/min zoomscale
        latdelta = MAX(MIN(latdelta, 80), 0.02);
        londelta = MAX(MIN(londelta, 80), 0.02);
        MKCoordinateSpan span = MKCoordinateSpanMake(latdelta, londelta);
    
        [self.mapView setRegion:MKCoordinateRegionMake(originalRegion.center, span) animated:YES];
    }
    

    This may not work perfectly like Apple's implementation but it should solve your issue.

    0 讨论(0)
  • 2020-12-03 03:56

    I've read about this before, though I've never actually tried it. Have a look at this article about a MKMapView with boundaries. It uses two delegate methods to check if the view has been scrolled by the user.

    http://blog.jamgraham.com/blog/2012/04/29/adding-boundaries-to-mkmapview

    The article describes an approach which is similar to what you've tried, so, sorry if you've already stumbled upon it.

    0 讨论(0)
  • 2020-12-03 03:57

    Swift 3.0 version of @Paras Joshi answer https://stackoverflow.com/a/11954355/3754976

    with small animation fix.

    class MapViewZoomCenter: MKMapView {
    
        var originalRegion: MKCoordinateRegion!
    
        override func awakeFromNib() {
           self.configureView()
        }
    
        func configureView() {
            isZoomEnabled = false
            self.registerZoomGesture()
        }
    
        ///Register zoom gesture
        func registerZoomGesture() {
            let recognizer = UIPinchGestureRecognizer(target: self, action:#selector(MapViewZoomCenter.handleMapPinch(recognizer:)))
            self.addGestureRecognizer(recognizer)
        }
    
        ///Zoom in/out map
        func handleMapPinch(recognizer: UIPinchGestureRecognizer) {
    
            if (recognizer.state == .began) {
                self.originalRegion = self.region;
            }
    
            var latdelta: Double = originalRegion.span.latitudeDelta / Double(recognizer.scale)
            var londelta: Double = originalRegion.span.longitudeDelta / Double(recognizer.scale)
    
            //set these constants to appropriate values to set max/min zoomscale
            latdelta = max(min(latdelta, 80), 0.02);
            londelta = max(min(londelta, 80), 0.02);
    
            let span = MKCoordinateSpanMake(latdelta, londelta)
    
            self.setRegion(MKCoordinateRegionMake(originalRegion.center, span), animated: false)
    
        }
    }
    
    0 讨论(0)
提交回复
热议问题