1

我有一个包含在 tabBarController 中的 viewController(带有 mapView)(我的 viewController 称为 MapViewController 是 TabBarController 的一部分)。

我的 TabBarController 包含在 navigationController 中。

我创建了(通过委托方法 MKMapViewDelegate)添加到地图的 annotationView。我允许标注显示标题和副标题。标题和副标题取自对我的数据库进行的查询。从这些查询中,我得到标题、详细信息、ID(字符串版本)和图像。

我在每个注释的标注中设置标题和副标题没有问题。

但是当我将每个标注的图像设置为 LeftCalloutAccessoryView 时,系统会为我提供所有相同的图像。

而且,当我点击 RightCalloutAccessoryView(这是一个推送到另一个 viewController 的按钮)时,它应该作为新窗口导航控制器打开(所以推送),给我错误的 ID(另一个注释的 ID),因此是新窗口中的详细信息错误。

我知道这可能会解释它,所以有点难以理解,但这里是代码:

    + (CGFloat)annotationPadding;
    {
        return 10.0f;
    }

    + (CGFloat)calloutHeight;
    {
        return 40.0f;
    }


    - (void)viewDidLoad
    {
        [super viewDidLoad];
        [_mapView setDelegate:self];
        _arrPosters = [[NSMutableArray alloc] init];
        _script = [[DBGMScr alloc] init]; //Is a class that provides script database connection
//With fieldsRequest I get the array with the field results sought
        _arrID = [[NSMutableArray alloc] initWithArray:[_script fieldsRequest:@"SELECT ID FROM Event ORDER BY DateTime DESC"]];
        _arrEventNames = [[NSMutableArray alloc] initWithArray:[_script fieldsRequest:@"SELECT Name FROM Event ORDER BY DateTime DESC"]];
        _arrEventLatitude = [[NSMutableArray alloc] initWithArray:[_script fieldsRequest:@"SELECT Latitude FROM Event ORDER BY DateTime DESC"]];
        _arrEventLongitude = [[NSMutableArray alloc] initWithArray:[_script fieldsRequest:@"SELECT Longitude FROM Event ORDER BY DateTime DESC"]];
        _arrEventDescription = [[NSMutableArray alloc] initWithArray:[_script fieldsRequest:@"SELECT Description FROM Event ORDER BY DateTime DESC"]];

        for (int i = 0; i < [_arrID count]; i++) {

//With getPoster I get the poster for the event (located on the server) by using a script (it works perfectly), and add it to the array of posters
        UIImage *poster = [_script getPoster:_arrID[i]];


            [_arrPosters insertObject:poster atIndex:i];
        }

        for (int i = 0; i < [_arrID count]; i++) {


            MKPointAnnotation *aAnnotationPoint = [[MKPointAnnotation alloc] init];
            CLLocationCoordinate2D theCoordinate;
            theCoordinate.latitude = [_arrEventLatitude[i] doubleValue];
            theCoordinate.longitude = [_arrEventLongitude[i] doubleValue];
            aAnnotationPoint.coordinate = theCoordinate;
            aAnnotationPoint.title = _arrEventNames[i];

            aAnnotationPoint.subtitle = _arrEventDescription[i];

            // Add the annotationPoint to the map
            [_mapView addAnnotation:aAnnotationPoint];

        }

    }


    #pragma mark - MKMapViewDelegate



    -(MKAnnotationView*)mapView:(MKMapView*)mapView viewForAnnotation:(id<MKAnnotation>)annotation {

        CGRect myRect = CGRectMake(-20, -20, 40, 40);

        EventAnnotationView *viewAnno = [[EventAnnotationView alloc] initWithFrame:myRect];

        viewAnno.canShowCallout = YES;
        UIButton* rightButton = [UIButton buttonWithType:
                                 UIButtonTypeDetailDisclosure];
        viewAnno.rightCalloutAccessoryView = rightButton;
        UIImageView *iconView = [[UIImageView alloc] initWithFrame:myRect];
        iconView.image = defaultImage; //defaultImage for now, but I want to show a different image for each annotation
        viewAnno.leftCalloutAccessoryView = iconView;
        NSString *viewTitle = [viewAnno.annotation title];

        return viewAnno;
    }


    - (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control
    {

        EventViewerViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"EventViewer"];
        [self.navigationController pushViewController:controller animated:YES];
        NSDictionary *selection = @{@"ID" : _arrID[_eventIndex] //but it get an incorrect ID,
                                    @"eventName" : _arrEventNames[_eventIndex]};

        [controller setValue:selection forKey:@"selection"];
    }

    -(void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view
    {
    //_eventIndex is an NSInteger declared in file .h
        _eventIndex = [_mapView.annotations indexOfObject:view.annotation];


    }

EventAnnotationView 是 MKAnnotationView 的子类:

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // make sure the x and y of the CGRect are half it's
        // width and height, so the callout shows when user clicks
        // in the middle of the image
        CGRect  viewRect = CGRectMake(-20, -20, 40, 40);
        UIImageView* imageView = [[UIImageView alloc] initWithFrame:viewRect];

        // keeps the image dimensions correct
        // so if you have a rectangle image, it will show up as a rectangle,
        // instead of being resized into a square
        imageView.contentMode = UIViewContentModeScaleAspectFit;

        _imageView = imageView;

        [self addSubview:imageView];
    }
    return self;
}

- (void)setImage:(UIImage *)image
{
    // when an image is set for the annotation view,
    // it actually adds the image to the image view
    _imageView.image = image;
}
4

1 回答 1

2

这里有几个问题,但首先回答您的两个主要问题:

  1. leftCalloutAccessoryView为每个注释设置不同的图像,您需要查看annotation传递给viewForAnnotation委托方法的参数中的某些值。在最粗略的水平上,您可以根据值设置图像annotation.title(例如,如果标题为“A”,则将图像设置为“1”,否则如果标题为“B”,则将图像设置为“2”等)。
  2. 由于以下代码,点击rightCalloutAccessoryView会为您提供错误的事件 ID didSelectAnnotationView

    _eventIndex = [_mapView.annotations indexOfObject:view.annotation];
    

    上面的代码假定annotations地图视图返回的数组与您添加注释的顺序完全相同(并且它只包含showsUserLocation添加的注释,如果是,则不是这种情况YES)。这个假设是错误的。不要假设annotations数组有任何特定的顺序——不要依赖它的顺序。

    更好的解决方案是使用注释对象本身,您可以calloutAccessoryControlTapped使用view.annotation.

    由于您需要 Event 的 Id 并且默认MKPointAnnotation类显然没有任何属性可以将其存储在任何地方,因此您应该创建自己的类(例如EventAnnotation),该类实现MKAnnotation并添加您需要为每个注释知道的所有事件属性。然后,您将能够使用annotation地图视图的委托方法中的对象直接访问特定于事件的值,而无需尝试引用回原始数组。

    该类EventAnnotation可以这样声明:

    @interface EventAnnotation : NSObject<MKAnnotation>
    @property (assign) CLLocationCoordinate2D coordinate;
    @property (copy) NSString *title;
    @property (copy) NSString *subtitle;
    @property (copy) NSString *eventId;
      //Not sure if your event id is a string.
      //Change to "@property (assign) int eventId;" if it's an integer.
    @end
    

    然后创建注释,创建一个EventAnnotation代替MKPointAnnotation

    EventAnnotation *aAnnotationPoint = [[EventAnnotation alloc] init];
    CLLocationCoordinate2D theCoordinate;
    theCoordinate.latitude = [_arrEventLatitude[i] doubleValue];
    theCoordinate.longitude = [_arrEventLongitude[i] doubleValue];
    aAnnotationPoint.coordinate = theCoordinate;
    aAnnotationPoint.title = _arrEventNames[i];
    aAnnotationPoint.subtitle = _arrEventDescription[i];
    
    //store event id in the annotation itself...
    aAnnotationPoint.eventId = _arrID[i];
    
    [_mapView addAnnotation:aAnnotationPoint];
    

    最后,在calloutAccessoryControlTapped(您不需要实现didSelectAnnotationView)中,您可以这样做:

    EventViewerViewController *controller = [self.storyboard instantiateViewControllerWithIdentifier:@"EventViewer"];
    [self.navigationController pushViewController:controller animated:YES];
    
    //cast the view's annotation object to EventAnnotation...
    EventAnnotation *eventAnn = (EventAnnotation *)view.annotation;
    
    NSDictionary *selection = @{@"ID" : eventAnn.eventId,
                                @"eventName" : eventAnn.title };
    
    [controller setValue:selection forKey:@"selection"];
    


您应该做的另一件事是在创建视图后立即viewForAnnotation显式设置视图的属性。annotation尽管它可能会以某种方式自动分配,但为了安全起见,我宁愿明确地这样做:

EventAnnotationView *viewAnno = [[EventAnnotationView alloc] initWithFrame:myRect];
viewAnno.annotation = annotation;  // <-- add this



两个不相关的点:

  • 与其为每个事件属性(ID、名称、描述、纬度等)创建一个单独的数组,我强烈建议您创建一个包含所有这些属性的“事件”类,然后您可以只创建一个事件对象数组。事实上,您可以让这个 Event 类自己实现 MKAnnotation(而不是创建两个类 Event 和 EventAnnotation)。
  • 该代码正在为每一列执行单独的 SQL 查询。除了检索到的列之外,所有查询都是相同的。通过在单个 SQL 查询中获取所有列(例如SELECT ID, Name, Latitude, Longitude, Description FROM Event ORDER BY DateTime DESC),可以大大改善这一点。我不熟悉这DBGMScr门课,但你应该认真研究一下。如果该表包含 500 个注释和 5 列,那么您目前总共检索 2500 行,而一次可能只有 500 行。
于 2013-10-05T03:08:31.577 回答