【问题标题】:Map annotation display all the same image/pins for all points地图注释显示所有点的所有相同图像/图钉
【发布时间】:2025-12-21 10:45:11
【问题描述】:

我有一个条件语句来在下面的方法中添加地图注释图标/图钉。我遇到的问题是地图上填充了所有相同的图标。它应该检测猫 ID 并根据检测到的猫 ID 显示图标。我不确定问题出在哪里,因为这在 iOS 6 中确实有效,而现在在 iOS 7 中,地图只显示所有相同的注释图标图像。

- (MKAnnotationView *) mapView:(MKMapView *)mapingView viewForAnnotation:(id <MKAnnotation>) annotation {
annView = nil;
if(annotation != mapingView.userLocation)
{
    
    static NSString *defaultPinID = @"";
    annView = (MKAnnotationView *)[mapingView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
    if ( annView == nil )
        annView = [[MKAnnotationView alloc]
                   initWithAnnotation:annotation reuseIdentifier:defaultPinID] ;
    
    
    UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    [rightButton setTitle:annotation.title forState:UIControlStateNormal];
 
    annView.rightCalloutAccessoryView = rightButton;
    
    MyAnnotation* annotation= [MyAnnotation new];
    
    annotation.catMapId = categoryIdNumber;
    NSLog(@"categoryIdNumber %@",categoryIdNumber);
    NSLog(@"annotation.catMapId %@",annotation.catMapId);

    
        if (annotation.catMapId == [NSNumber numberWithInt:9]) {
            annView.image = [UIImage imageNamed:@"PIN_comprare.png"];
            
            NSLog(@"annview 9");
            
        }
        
        else if (annotation.catMapId == [NSNumber numberWithInt:10]) {
            annView.image = [UIImage imageNamed:@"PIN_mangiare.png"];
            
            NSLog(@"annview 10");
            
        }
        
        else if (annotation.catMapId == [NSNumber numberWithInt:11]) {
            annView.image = [UIImage imageNamed:@"PIN_visitare.png"];
            
            NSLog(@"annview 11");
            
        }
        
        else if (annotation.catMapId == [NSNumber numberWithInt:12]) {
            annView.image = [UIImage imageNamed:@"PIN_vivere.png"];
            
            NSLog(@"annview 12");
            
        }
 
    annView.canShowCallout = YES;
    
}

return annView;

}

【问题讨论】:

标签: ios objective-c mapkit mkannotation


【解决方案1】:

如果,如您所说,“这在 iOS 6 中确实有效”,您应该认为它确实(或似乎)是幸运的,并且这种设置注释图像的方法不应该是依赖于任何版本。

虽然@Ar Ma 应该设置注释视图的annotation 属性是正确的(以防视图被重复使用),但这并不能解决主要问题。

注释视图的image 是根据categoryIdNumber 的值设置的,这似乎是viewForAnnotation 委托方法之外的一些变量

不能假设:

  1. viewForAnnotation 将在您调用addAnnotation 后立即调用。即使在 iOS 6 或更早版本中,也无法保证这一点。
  2. viewForAnnotation 对于每个注释只会被调用一次。当用户平移或缩放地图时,可以为同一个注释多次调用委托方法,并且注释会返回到屏幕上。
  3. viewForAnnotation 将按照添加注释的相同顺序调用。这是第 1 点和第 2 点的结果。

我假设在您调用addAnnotation 之前,categoryIdNumber 设置正确,然后基于上述错误假设,viewForAnnotation 使用categoryIdNumber 设置图像。

发生的情况是地图视图在某个时间调用viewForAnnotation,此时所有或部分addAnnotation 调用已完成,此时categoryIdNumber 可能是与添加的最后一个注释相关的值,并且所有注释使用适用于最后一个注释的图像。


要解决此问题(无论 iOS 版本),您必须将正确的categoryIdNumber 值放入每个注释对象调用addAnnotation .

看起来您的注释类是 MyAnnotation 并且您已经有一个 catMapId 属性。

您必须在调用addAnnotation 之前 在注解中设置此属性——而不是viewForAnnotation 方法中,这为时已晚. (顺便说一句,您正在创建一个 MyAnnotation 对象 inside viewForAnnotation 方法,这是毫无意义的。)


所以你在哪里创建和添加注释(不在viewForAnnotation):

MyAnnotation* myAnn = [[MyAnnotation alloc] init];
myAnn.coordinate = ...
myAnn.title = ...
myAnn.catMapId = categoryIdNumber;  // <-- set catMapId BEFORE addAnnotation
[mapView addAnnotation:myAnn];

那么viewForAnnotation中的代码应该是这样的:

- (MKAnnotationView *) mapView:(MKMapView *)mapingView viewForAnnotation:(id <MKAnnotation>) annotation
{
    annView = nil;
    if(annotation != mapingView.userLocation)
    {

        static NSString *defaultPinID = @"MyAnnId";
        annView = (MKAnnotationView *)[mapingView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
        if ( annView == nil )
        {
            annView = [[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] ;
            annView.canShowCallout = YES;
        }
        else
        {
            //view is being re-used, re-set annotation to current...
            annView.annotation = annotation;
        }

        UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
        [rightButton setTitle:annotation.title forState:UIControlStateNormal];

        annView.rightCalloutAccessoryView = rightButton;


        //Make sure we have a MyAnnotation-type annotation
        if ([annotation isKindOfClass:[MyAnnotation class]])
        {
            //Do not CREATE a local MyAnnotation object here.
            //Instead, get the catMapId from the annotation object
            //that was PASSED INTO the delegate method.
            //MyAnnotation* annotation= [MyAnnotation new];
            //annotation.catMapId = categoryIdNumber;

            MyAnnotation *myAnn = (MyAnnotation *)annotation;

            //The value of the external variable categoryIdNumber is irrelevant here.
            //NSLog(@"categoryIdNumber %@",categoryIdNumber);

            NSLog(@"myAnn.catMapId %@",myAnn.catMapId);


            //Put the NSNumber value into an int to simplify the code below.
            int myAnnCatMapId = [myAnn.catMapId intValue];

            NSString *imageName = nil;
            switch (myAnnCatMapId)
            {
                case 9:
                {
                    imageName = @"PIN_comprare.png";
                    break;
                }

                case 10:
                {
                    imageName = @"PIN_mangiare.png";
                    break;
                }

                case 11:
                {
                    imageName = @"PIN_mangiare.png";
                    break;
                }

                case 12:
                {
                    imageName = @"PIN_vivere.png";
                    break;
                }

                default:
                {
                    //set some default image for unknown cat ids...
                    imageName = @"default.png";
                    break;
                }
            }

            annView.image = [UIImage imageNamed:imageName];

            NSLog(@"annview %d", myAnnCatMapId);
        }
    }

    return annView; 
}

【讨论】:

  • 这很好用!感谢您抽出时间帮助我:)
  • 不客气。您可能已经解决了这个问题,但我在 switch 语句中犯了一个小错误。对于案例 11,它应该是 visitare 图像。
【解决方案2】:

在最后添加这一行:

annView.annotation = annotation;

【讨论】:

  • 谢谢。注释确实显示,但都是相同的图钉图像。对于不同的类别 ID,它应该是不同的 pin 图像。
  • 我添加了一个显示引脚的屏幕截图,但它们都是相同的引脚,并且应该根据条件而有所不同。
【解决方案3】:

同样的麻烦,对我来说决定不使用

pinView.animatesDrop   = YES;

自定义图标不适用于我的动画拖放。

【讨论】:

    【解决方案4】:

    如果有人需要将 MapView 注释用作 tableView,即在地图上显示一组点。

    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        let identifier = MKMapViewDefaultAnnotationViewReuseIdentifier
        if let annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? TrailAnnotationView {
                ///TrailAnnotation has an object of type trail ( can be any model you ///want ). and so is TrailAnnotationView
    ///inside TrailAnnotationView we extract data from trail and display it.
            annotationView.trail = (annotation as? TrailAnnotation)?.trail
            annotationView.annotation = annotation
            return annotationView
        }
        let annotationView = TrailAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        annotationView.trail = (annotation as? TrailAnnotation)?.trail
        annotationView.canShowCallout = true
        return annotationView
    }
    

    这是一个 TrailAnnotationView

    protocol AnnotationViewProtocol {
        func didTapOnAnnotation()
    }
    
    class TrailAnnotationView: MKPinAnnotationView {
    
    /*
    // Only override draw() if you perform custom drawing.
    // An empty implementation adversely affects performance during animation.
    override func draw(_ rect: CGRect) {
        // Drawing code
    }
    */
    
    var trail: TrailModel? = nil
    
    
    override var annotation: MKAnnotation? { didSet { configureDetailView() } }
    
    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        configure()
    }
    
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        configure()
    }
    
    }
    
    private extension TrailAnnotationView {
        func configure() {
            canShowCallout = true
            configureDetailView()
        }
    
    func configureDetailView() {
        guard let annotation = annotation else { return }
    
        let rect = CGRect(origin: .zero, size: CGSize(width: 300, height: 200))
    
        let snapshotView = UIView()
        snapshotView.translatesAutoresizingMaskIntoConstraints = false
    
        if let trail = self.trail, !trail.imageUrl.isEmpty {
            AppUtils.sharedInstance.fetchImageFor(path: trail.imageUrl) { (image) in
                guard let imageData = image else { return }
                DispatchQueue.main.async {
                    let imageView = UIImageView(frame: rect)
                    imageView.image = imageData
                    snapshotView.addSubview(imageView)
                }
            }
        } else {
            let options = MKMapSnapshotter.Options()
            options.size = rect.size
            options.mapType = .satelliteFlyover
            options.camera = MKMapCamera(lookingAtCenter: annotation.coordinate, fromDistance: 250, pitch: 65, heading: 0)
    
            let snapshotter = MKMapSnapshotter(options: options)
            snapshotter.start { snapshot, error in
                guard let snapshot = snapshot, error == nil else {
                    print(error ?? "Unknown error")
                    return
                }
    
                let imageView = UIImageView(frame: rect)
                imageView.image = snapshot.image
                snapshotView.addSubview(imageView)
            }
        }
    
        detailCalloutAccessoryView = snapshotView
        NSLayoutConstraint.activate([
            snapshotView.widthAnchor.constraint(equalToConstant: rect.width),  
            snapshotView.heightAnchor.constraint(equalToConstant: rect.height)
        ])
    }
    }
    

    【讨论】: