问题
I have a MKCircle on MKMapView. It is user-draggable, but if the user drags the area outside the circle, the map should be moving instead.
- (IBAction)createPanGestureRecognizer:(id)sender
{
_mapView.scrollEnabled=NO;
_panRecognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:@selector(respondToPanGesture:)];
[_mapView addGestureRecognizer:_panRecognizer];
}
-(void)respondToPanGesture:(UIPanGestureRecognizer*)sender {
static CGPoint originalPoint;
if (sender.state == UIGestureRecognizerStateBegan) {
CGPoint point = [sender locationInView:_mapView];
CLLocationCoordinate2D tapCoordinate = [_mapView convertPoint:point toCoordinateFromView:_mapView];
CLLocation *tapLocation = [[CLLocation alloc] initWithLatitude:tapCoordinate.latitude longitude:tapCoordinate.longitude];
CLLocationCoordinate2D originalCoordinate = [_circle coordinate];
CLLocation *originalLocation = [[CLLocation alloc] initWithLatitude:originalCoordinate.latitude longitude:originalCoordinate.longitude];
if ([tapLocation distanceFromLocation:originalLocation] > [_circle radius]) {
_mapView.scrollEnabled=YES;
_isAllowedToMove=NO;
}
else if ([tapLocation distanceFromLocation:originalLocation] < [_circle radius]) {
originalPoint = [_mapView convertCoordinate:originalCoordinate toPointToView:sender.view];
_isAllowedToMove=YES;
}
}
if (sender.state == UIGestureRecognizerStateChanged) {
if (_isAllowedToMove)
{
CGPoint translation = [sender translationInView:sender.view];
CGPoint newPoint = CGPointMake(originalPoint.x + translation.x, originalPoint.y + translation.y);
CLLocationCoordinate2D newCoordinate = [_mapView convertPoint:newPoint toCoordinateFromView:sender.view];
MKCircle *circle2 = [MKCircle circleWithCenterCoordinate:newCoordinate radius:[_circle radius]];
[_mapView addOverlay:circle2];
[_mapView removeOverlay:_circle];
_circle = circle2;
}
}
if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled) {
_mapView.scrollEnabled=NO;
_isAllowedToMove=NO;
}
}
Dragging the circle works fine, but when trying to drag the map, it stays still. My assumption is that
_mapView.scrollEnabled=YES;
makes the map draggable, but it needs another drag gesture to be started. How to accomplish this without losing the ability to move the circle?
回答1:
To make it so that the map can be dragged if the user starts dragging outside the circle (and to not drag the map if the user starts dragging inside the circle), don't disable scrollEnabled
from the beginning -- leave it on (until dragging starts and we can decide whether to disable or not).
In order to leave scrollEnabled
on initially (ie. let the map do its own panning) and to add our own gesture recognizer, we'll need to implement shouldRecognizeSimultaneouslyWithGestureRecognizer
and return YES
.
The updated createPanGestureRecognizer:
would look like this:
- (IBAction)createPanGestureRecognizer:(id)sender
{
//_mapView.scrollEnabled=NO; // <-- do NOT disable scrolling here
_panRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(respondToPanGesture:)];
_panRecognizer.delegate = self; // <-- to implement shouldRecognize
[_mapView addGestureRecognizer:_panRecognizer];
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
{
return YES;
}
Then in our gesture handler, when the gesture begins, enable or disable scrollEnabled
based on where the pan starts (outside or inside the circle):
if ([tapLocation distanceFromLocation:originalLocation] > [_circle radius]) {
_mapView.scrollEnabled=YES;
_isAllowedToMove=NO;
}
else //if ([tapLocation distanceFromLocation:originalLocation] < [_circle radius]) {
//NOTE: It's not really necessary to check if distance is less than radius
// in the ELSE part since in the IF we checked if it's greater-than.
// If we get to the ELSE, we know distance is <= radius.
// Unless for some reason you want to handle the case where
// distance exactly equals radius differently but this is unlikely.
{
originalPoint = [_mapView convertCoordinate:originalCoordinate toPointToView:sender.view];
_mapView.scrollEnabled=NO; // <-- disable scrolling HERE
_isAllowedToMove=YES;
}
Finally, always re-enable scrollEnabled
when a gesture ends (just in case).
When the next gesture starts, it will be re-disabled if necessary:
if (sender.state == UIGestureRecognizerStateEnded || sender.state == UIGestureRecognizerStateFailed || sender.state == UIGestureRecognizerStateCancelled) {
_mapView.scrollEnabled=YES; // <-- enable instead of disable
_isAllowedToMove=NO;
}
Note that if the user is allowed to call createPanGestureRecognizer:
multiple times, you should probably move the creation and addition of the recognizer somewhere else (viewDidLoad
maybe) otherwise multiple instances will get added to the map.
Alternatively, change the button to a toggle so that if "move" mode is ON, it removes the gesture recognizer from the map instead of creating and adding it.
回答2:
Updated answer using swift 4
Includes some changes:
panGesture.maximumNumberOfTouches = 1
to avoid detecting pinch to zoom as pan gesture
Screen points used for active area where pan gesture can be started.
private func addPanGestureRecognizer(to map: MKMapView) {
let panGesture = UIPanGestureRecognizer(target: self, action: #selector(respondToPanGesture(_:)))
panGesture.maximumNumberOfTouches = 1
panGesture.delegate = self
map.addGestureRecognizer(panGesture)
}
@objc private func respondToPanGesture(_ sender: UIPanGestureRecognizer) {
guard let map = self.map else {
return
}
let circlePoint = map.convert(startPoint, toPointTo: map) // circle center in View coordinate system
switch sender.state {
case .began:
let point = sender.location(in: map)
// Set touch radius in points (20), not meters to support different map zoom levels
if point.radiusContainsPoint(radius: 20, point: circlePoint) {
// set class property to store initial circle position before start dragging
initialDragPoint = circlePoint
map.isScrollEnabled = false
isDragEnabled = true
}
else {
map.isScrollEnabled = true
isDragEnabled = false
}
case .changed:
if isDragEnabled {
let translation = sender.translation(in: map)
let newPoint = CGPoint(x: initialDragPoint.x + translation.x, y: initialDragPoint.y + translation.y)
let updatedCoordinate = map.convert(newPoint, toCoordinateFrom: map)
let newOverlay = makeCircleOverlay(at: updatedCoordinate)
map.remove(circleOverlay)
map.add(newOverlay)
circleOverlay = newOverlay
}
case .ended, .failed, .cancelled:
map.isScrollEnabled = true
isDragEnabled = false
case .possible:
break
}
}
// MARK: - UIGestureRecognizerDelegate
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
return true
}
来源:https://stackoverflow.com/questions/22441059/moving-mkcircle-on-mkmapview-and-dragging-mkmapview