地图缩放时如何调整MKAnnotationView图片大小?

7

我现有的内容

我在地图上有大约150个MKAnnotationViews。 每个MKAnnotationView都有一个替换默认图钉的图像。

现在发生了什么

当地图缩小时,MKAnnotationViews变小,反之亦然。

我希望发生的事情

我希望它相反。因为当地图很小时,我希望MKAnnotationViews会变得更小,这样用户就可以看到它们所有的细节,而当用户放大地图时,我希望它们会变得更大。

我目前拥有的代码

我知道如何获取缩放变化,并且我知道我可以将“pMapView.region.span.latitudeDelta”作为缩放量的参考,并且我知道我可以更改annotationView.frame。

-(void)mapView:(MKMapView *)pMapView regionDidChangeAnimated:(BOOL)animated{
    NSLog(@"mapView.region.span.latitudeDelta = %f",pMapView.region.span.latitudeDelta);
    for (id <MKAnnotation> annotation in pMapView.annotations) {
        MKAnnotationView *annotationView  = [pMapView viewForAnnotation: annotation];
    }
}

有人能帮我一下吗?关于IT技术的问题。 谢谢 shani

6个回答

27
实际上,在默认的MKMapView上,标注(例如,图钉或图像)和弹出窗口(例如,气泡)在缩放时保持相同的大小。它们不会缩放。但是我理解你的意思-相对于地图,它们似乎随着地图缩小而增长,随着地图放大而缩小。
因此,有两种解决方法,它们的工作方式略有不同:
1. 实现MKMapViewDelegate协议引用中的-(void)mapView:(MKMapView *)pMapView regionDidChangeAnimated:(BOOL)animated方法-您已经完成了这一步。 2. 将UIPinchGestureRecognizer附加到MKMapView对象上,然后实现该操作。
选项#1- mapView:regionDidChangeAnimated:将为滚动或缩放事件调用-基本上任何时候地图区域发生更改,如名称所示。这会导致图标稍微不太平滑地调整大小,因为地图事件触发较少。
我更喜欢选项#2-将UIPinchGestureRecognizer附加到MKMapView对象上,然后实现该操作。捏合手势事件会相当快地触发,因此您可以平稳地调整图标大小。它们仅在识别出捏合事件时触发-因此它们不会在滚动事件期间触发。
调用的操作方法必须符合以下签名之一:

- (void)handleGesture;
- (void)handleGesture:(UIGestureRecognizer *)gestureRecognizer;

你必须小心不要覆盖地图的默认缩放行为。请参阅此文章:"UIMapView:UIPinchGestureRecognizer未被调用",了解更多信息。简短的答案是,你必须实现shouldRecognizeSimultaneouslyWithGestureRecognizer:并返回YES。

总之,这里有一些示例代码:

// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad
{
    [super viewDidLoad];

    self.mapView.mapType = MKMapTypeStandard;   // also MKMapTypeSatellite or MKMapTypeHybrid

    // Add a pinch gesture recognizer
    UIPinchGestureRecognizer *pinchRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchGesture:)];
    pinchRecognizer.delegate = self;
    [self.mapView addGestureRecognizer:pinchRecognizer];
    [pinchRecognizer release];
}

#pragma mark -
#pragma mark UIPinchGestureRecognizer

- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinchRecognizer {
    if (pinchRecognizer.state != UIGestureRecognizerStateChanged) {
        return;
    }

    MKMapView *aMapView = (MKMapView *)pinchRecognizer.view;

    for (id <MKAnnotation>annotation in aMapView.annotations) {
        // if it's the user location, just return nil.
        if ([annotation isKindOfClass:[MKUserLocation class]])
            return;

        // handle our custom annotations
        //
        if ([annotation isKindOfClass:[MKPointAnnotation class]])
        {
            // try to retrieve an existing pin view first
            MKAnnotationView *pinView = [aMapView viewForAnnotation:annotation];
            //Format the pin view
            [self formatAnnotationView:pinView forMapView:aMapView];
        }
    }    
}

- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
    return YES;
}

在这一点上,您有几个选项来调整注释的大小。以下两个代码示例都依赖于Troy Brant的代码以获取MKMapView的缩放级别
  1. 使用变换同时调整注释图像和标题。个人认为,变换可以产生更清晰的调整效果。但在大多数情况下,不需要调整标题。
  2. 仅调整注释图像 - 我使用Trevor Harmon的正确方式调整UIImage的大小,但我认为它的调整效果不如第一个方法。
以下是更多示例代码:
- (void)formatAnnotationView:(MKAnnotationView *)pinView forMapView:(MKMapView *)aMapView {
    if (pinView)
    {
        double zoomLevel = [aMapView zoomLevel];
        double scale = -1 * sqrt((double)(1 - pow((zoomLevel/20.0), 2.0))) + 1.1; // This is a circular scale function where at zoom level 0 scale is 0.1 and at zoom level 20 scale is 1.1

        // Option #1
        pinView.transform = CGAffineTransformMakeScale(scale, scale);

        // Option #2
        UIImage *pinImage = [UIImage imageNamed:@"YOUR_IMAGE_NAME_HERE"];
        pinView.image = [pinImage resizedImage:CGSizeMake(pinImage.size.width * scale, pinImage.size.height * scale) interpolationQuality:kCGInterpolationHigh];
    }
}

如果这个方法有效,请不要忘记将其标记为答案。

嗨,我会尝试这个,谢谢你的好答案。不用担心批准的问题 :) - shannoga
5
没错,在iOS6上已经不再适用了。有人想出解决方法了吗? - Bach

4

@Funktional在Swift 3中的回答:

class MapViewController: UIViewController: UIGestureRecognizerDelegate {

    @IBOutlet weak var mapView: MKMapView!

    override func viewDidLoad() {
        super.viewDidLoad()

        // You can also add this gesture recognizer and set the delegate via storyboard
        let pinchGR = UIPinchGestureRecognizer(target: self, action: #selector(handlePinchGesture))
        pinchGR.delegate = self
        self.mapView.addGestureRecognizer(pinchGR)
    }

    // link as @IBAction when added via storyboard
    func handlePinchGesture(_ sender: UIPinchGestureRecognizer) {
        if sender.state == .ended {
            for annotation in mapView.annotations {
                if annotation is MKUserLocation {
                    continue
                }
                guard let annotationView = self.mapView.view(for: annotation) else { continue }
                let scale = -1 * sqrt(1 - pow(mapView.zoomLevel / 20, 2.0)) + 1.4
                annotationView.transform = CGAffineTransform(scaleX: CGFloat(scale), y: CGFloat(scale))
            }
        }
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
}

extension MKMapView {
    var zoomLevel: Double {
        return log2(360 * ((Double(self.frame.size.width) / 256) / self.region.span.longitudeDelta)) - 1
    }
}

1

另一种方法是重新调整注释视图图像属性的大小。一个例子可以在这篇文章中看到。


1

抱歉我的英语不好...但我只是想帮助别人。

非常感谢你,Funktional。你的回答很棒...在iOS5上完美运行!但这在iOS6上无效,目前似乎没有解决此问题的方法。

但最终,我用一种愚蠢/静态的方式解决了它,在两个iOS上都可以正常工作。我不打算解释太多,这就是我在项目中所做的。

  1. mkannotation的图像名称为pin.png(大小为20x20)

  2. 我创建了五个不同大小的图像,用于不同缩放级别(18x18、16x16、14x14、12x12、10x10),它们的名称分别为pin-5.png、pin-4.png、pin-3.png、pin-2.png、pin-1.png。

  3. 我删除了formatAnnotationView中关于计算比例的所有代码,并添加了这一行:

    NSString* imageName = 
        [NSString stringWithFormat:@"%@%@.png", 
        [imageName substringToIndex:[imageName length]-4 ], 
        [self getImageLevel]];
    
  4. 此外,将以下代码:

    UIImage *pinImage = [UIImage imageNamed:@"YOUR_IMAGE_NAME_HERE"];

    更改为:

    UIImage *pinImage = [[UIImage alloc] initWithContentsOfFile:imageName];

  5. 添加此函数:

    -(NSString *)getImageLevel{
    
    NSString* output;
    
    double zoomLevel = [self getZoomLevel:_mapview];
    
    
    if(zoomLevel >= 19.0)
        output = @"";
    else if(zoomLevel < 19.0 && zoomLevel >= 18.0)
        output = @"-5";
    else if(zoomLevel < 18.0 && zoomLevel >= 17.0)
        output = @"-4";
    else if(zoomLevel < 17.0 && zoomLevel >= 16.0)
        output = @"-3";
    else if(zoomLevel < 16.0 && zoomLevel >= 15.0)
        output = @"-2";
    else 
        output = @"-1";
    
    return output;}
    

很抱歉代码和英语都不太好。


1
在Swift 3中,这对我有效。我的图像在19级时是100%,因此1/19给出0.5263158,这是我的线性比例尺:
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {    
  if annotation is MKUserLocation {
    return nil
  }

  let mkView = MKAnnotationView(annotation: annotation, reuseIdentifier: "mkView")
  mkView.image = UIImage(named: "Foo")
  formatAnnotation(pinView: mkView, forMapView: mapView)
  return mkView;
}

func formatAnnotation(pinView: MKAnnotationView, forMapView: MKMapView) {
  let zoomLevel = forMapView.getZoomLevel()
  let scale = 0.05263158 * zoomLevel //Modify to whatever scale you need.
  pinView.transform = CGAffineTransform(scaleX: CGFloat(scale), y: CGFloat(scale))
}

它只适用于新创建的注释进行缩小操作,已经创建的注释在缩小时大小不同,你有什么解决方案吗? - Varun Naharia

0

我不会使用手势识别器,因为缩放有可能也会以编程方式发生,而且这种方法无法允许注释在缩放时调整大小,只有在完成缩放后才能调整。我会像这样使用代理方法mapViewDidChangeVisibleRegion(_ mapView: MKMapView):

 func mapViewDidChangeVisibleRegion(_ mapView: MKMapView) {
        for annotation in mkMapView!.annotations {
            guard let annotationView = self.mkMapView!.view(for: annotation) else { continue }
            let scale = -1 * sqrt(1 - pow(mkMapView!.zoomLevel / 19, 2.0)) + 1.4
            annotationView.transform = CGAffineTransform(scaleX: CGFloat(scale), y: CGFloat(scale))
        }
    }

你还需要这个扩展:

extension MKMapView {
    var zoomLevel: Double {
        return log2(360 * ((Double(self.frame.size.width) / 256) / self.region.span.longitudeDelta)) - 1
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接