Как настроить пузырь вызова для MKAnnotationView?

В настоящее время я работаю с mapkit и застрял.

У меня есть пользовательский вид аннотации, который я использую, и я хочу использовать свойство изображения для отображения точки на карте с помощью моего собственного значка. У меня это нормально работает. Но то, что я также хотел бы сделать, это переопределить вид вызова по умолчанию (пузырь, который отображается с заголовком/субтитрами при касании значка аннотации). Я хочу иметь возможность управлять самой выноской: mapkit предоставляет только доступ к левым и правым представлениям сопутствующих выносок, но не имеет возможности предоставить настраиваемое представление для пузыря выноска, или дать ему нулевой размер или что-то еще.

Моя идея состояла в том, чтобы переопределить selectAnnotation/deselectAnnotation в моем MKMapViewDelegate, а затем нарисовать собственное пользовательское представление, сделав вызов в моем пользовательском представлении аннотации. Это работает, но только когда canShowCallout установлен в YES в моем классе пользовательского вида аннотаций. Эти методы НЕ вызывают, если у меня установлен этот параметр на NO (это то, что я хочу, чтобы пузырь по умолчанию не был вычерчен). Поэтому у меня нет возможности узнать, коснулся ли пользователь моей точки на карте (выбрал ее) или коснулся точки, которая не является частью моих представлений аннотаций (отразила ее), не отображая отображение пузырьков по умолчанию.

Я попытался пойти по другому пути и просто обработать все события касания самостоятельно на карте, и я не могу заставить это работать. Я читал другие сообщения, связанные с улавливанием событий касания на карте, но они не совсем то, что я хочу. Есть ли способ перекопать карту, чтобы удалить пузырь выноски перед рисованием? Я в недоумении.

Любые предложения? Мне что-то не хватает?

Ответ 1

Существует еще более простое решение.

Создайте пользовательский UIView (для вашей выноски).

Затем создайте подкласс MKAnnotationView и переопределите setSelected следующим образом:

- (void)setSelected:(BOOL)selected animated:(BOOL)animated
{
    [super setSelected:selected animated:animated];

    if(selected)
    {
        //Add your custom view to self...
    }
    else
    {
        //Remove your custom view...
    }
}

Бум, работа выполнена.

Ответ 2

detailCalloutAccessoryView

В старые времена это было больно, но Apple решила его, просто проверьте документы на MKAnnotationView

view = MKPinAnnotationView(annotation: annotation, reuseIdentifier: identifier)
view.canShowCallout = true
view.detailCalloutAccessoryView = UIImageView(image: UIImage(named: "zebra"))

Действительно, это. Принимает любой UIView.

Ответ 3

Продолжая с @TappCandy блестяще простой ответ, если вы хотите анимировать свой пузырь так же, как и по умолчанию, я создал этот метод анимации:

- (void)animateIn
{   
    float myBubbleWidth = 247;
    float myBubbleHeight = 59;

    calloutView.frame = CGRectMake(-myBubbleWidth*0.005+8, -myBubbleHeight*0.01-2, myBubbleWidth*0.01, myBubbleHeight*0.01);
    [self addSubview:calloutView];

    [UIView animateWithDuration:0.12 delay:0.0 options:UIViewAnimationOptionCurveEaseOut animations:^(void) {
        calloutView.frame = CGRectMake(-myBubbleWidth*0.55+8, -myBubbleHeight*1.1-2, myBubbleWidth*1.1, myBubbleHeight*1.1);
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.1 animations:^(void) {
            calloutView.frame = CGRectMake(-myBubbleWidth*0.475+8, -myBubbleHeight*0.95-2, myBubbleWidth*0.95, myBubbleHeight*0.95);
        } completion:^(BOOL finished) {
            [UIView animateWithDuration:0.075 animations:^(void) {
                calloutView.frame = CGRectMake(-round(myBubbleWidth/2-8), -myBubbleHeight-2, myBubbleWidth, myBubbleHeight);
            }];
        }];
    }];
}

Это выглядит довольно сложно, но до тех пор, пока точка вашего пузыря выноски будет сконцентрирована на центре, вы должны просто заменить myBubbleWidth и myBubbleHeight своим собственным размером, чтобы он работал. И не забудьте убедиться, что ваши объекты в представлении имеют свойство autoResizeMask, равное 63 (т.е. "Все" ), чтобы они правильно масштабировались в анимации.

: - Джо

Ответ 4

Обнаружено, что это лучшее решение для меня. Вы должны будете использовать некоторые креативы, чтобы выполнять свои собственные настройки

В вашем подклассе MKAnnotationView вы можете использовать

- (void)didAddSubview:(UIView *)subview{
    int image = 0;
    int labelcount = 0;
    if ([[[subview class] description] isEqualToString:@"UICalloutView"]) {
        for (UIView *subsubView in subview.subviews) {
            if ([subsubView class] == [UIImageView class]) {
                UIImageView *imageView = ((UIImageView *)subsubView);
                switch (image) {
                    case 0:
                        [imageView setImage:[UIImage imageNamed:@"map_left"]];
                        break;
                    case 1:
                        [imageView setImage:[UIImage imageNamed:@"map_right"]];
                        break;
                    case 3:
                        [imageView setImage:[UIImage imageNamed:@"map_arrow"]];
                        break;
                    default:
                        [imageView setImage:[UIImage imageNamed:@"map_mid"]];
                        break;
                }
                image++;
            }else if ([subsubView class] == [UILabel class]) {
                UILabel *labelView = ((UILabel *)subsubView);
                switch (labelcount) {
                    case 0:
                        labelView.textColor = [UIColor blackColor];
                        break;
                    case 1:
                        labelView.textColor = [UIColor lightGrayColor];
                        break;

                    default:
                        break;
                }
                labelView.shadowOffset = CGSizeMake(0, 0);
                [labelView sizeToFit];
                labelcount++;
            }
        }
    }
}

И если subview является UICalloutView, тогда вы можете прикрутить его и что внутри него.

Ответ 5

У меня была та же проблема. В этом блоге есть серьезные сообщения в блогах по этой теме http://spitzkoff.com/craig/?p=81.

Просто использование MKMapViewDelegate не поможет вам здесь и подклассификация MKMapView, и попытка расширить существующую функциональность также не сработала для меня.

То, что я закончил, - создать мой собственный CustomCalloutView, который у меня есть поверх моего MKMapView. Вы можете стилизовать этот вид так, как хотите.

У моего CustomCalloutView есть метод, подобный этому:


- (void) openForAnnotation: (id)anAnnotation
{
    self.annotation = anAnnotation;
    // remove from view
    [self removeFromSuperview];

    titleLabel.text = self.annotation.title;

    [self updateSubviews];
    [self updateSpeechBubble];

    [self.mapView addSubview: self];
}

Он принимает объект MKAnnotation и устанавливает свой собственный заголовок, после чего он вызывает два других метода, которые довольно уродливы, которые регулируют ширину и размер содержимого выноски, а затем вытягивают речевой пузырь вокруг него в правильном положении.

Наконец, представление добавляется как subview в mapView. Проблема с этим решением заключается в том, что трудно сохранить выноску в правильном положении при прокрутке карты. Я просто скрываю выноску в методе делегата вида карты при изменении области, чтобы решить эту проблему.

Потребовалось некоторое время, чтобы решить все эти проблемы, но теперь выноска почти ведет себя как официальный, но у меня это в моем собственном стиле.

Ответ 6

В основном для решения этой проблемы необходимо:  a) Предотвратите появление пузыря по умолчанию.  b) Выясните, какая аннотация была нажата.

Я смог добиться этого: a) настройка canShowCallout на NO b) подклассирование, MKPinAnnotationView и переопределение прикосновенийBegan и touchEnd методов.

Примечание. Вам необходимо обрабатывать события касания для MKAnnotationView, а не MKMapView

Ответ 7

Я просто придумал подход, идея здесь

  // Detect the touch point of the AnnotationView ( i mean the red or green pin )
  // Based on that draw a UIView and add it to subview.
- (void)mapView:(MKMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
    CGPoint newPoint = [self.mapView convertCoordinate:selectedCoordinate toPointToView:self.view];
//    NSLog(@"regionWillChangeAnimated newPoint %f,%f",newPoint.x,newPoint.y);
    [testview  setCenter:CGPointMake(newPoint.x+5,newPoint.y-((testview.frame.size.height/2)+35))];
    [testview setHidden:YES];
}

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    CGPoint newPoint = [self.mapView convertCoordinate:selectedCoordinate toPointToView:self.view];
//    NSLog(@"regionDidChangeAnimated newPoint %f,%f",newPoint.x,newPoint.y);
    [testview  setCenter:CGPointMake(newPoint.x,newPoint.y-((testview.frame.size.height/2)+35))];
    [testview setHidden:NO];
}

- (void)mapView:(MKMapView *)mapView didSelectAnnotationView:(MKAnnotationView *)view 
{  
    NSLog(@"Select");
    showCallout = YES;
    CGPoint point = [self.mapView convertPoint:view.frame.origin fromView:view.superview];
    [testview setHidden:NO];
    [testview  setCenter:CGPointMake(point.x+5,point.y-(testview.frame.size.height/2))];
    selectedCoordinate = view.annotation.coordinate;
    [self animateIn];
}

- (void)mapView:(MKMapView *)mapView didDeselectAnnotationView:(MKAnnotationView *)view 
{
    NSLog(@"deSelect");
    if(!showCallout)
    {
        [testview setHidden:YES];
    }
}

Здесь - testview - это UIView размером 320x100 - showCallout - BOOL - [self animateIn]; - это функция, которая просматривает анимацию, например UIAlertView.

Ответ 8

Вы можете использовать leftCalloutView, установив annotation.text в @""

Ниже приведен пример кода:

pinView = (MKPinAnnotationView *)[mapView dequeueReusableAnnotationViewWithIdentifier:defaultPinID];
if(pinView == nil){
    pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:defaultPinID] autorelease];       
}
CGSize sizeText = [annotation.title sizeWithFont:[UIFont fontWithName:@"HelveticaNeue" size:12] constrainedToSize:CGSizeMake(150, CGRectGetHeight(pinView.frame))                                 lineBreakMode:UILineBreakModeTailTruncation];
pinView.canShowCallout = YES;    
UILabel *lblTitolo = [[UILabel alloc] initWithFrame:CGRectMake(2,2,150,sizeText.height)];
lblTitolo.text = [NSString stringWithString:ann.title];
lblTitolo.font = [UIFont fontWithName:@"HelveticaNeue" size:12];
lblTitolo.lineBreakMode = UILineBreakModeTailTruncation;
lblTitolo.numberOfLines = 0;
pinView.leftCalloutAccessoryView = lblTitolo;
[lblTitolo release];
annotation.title = @" ";            

Ответ 9

Я вытолкнул свою вилку из превосходного SMCalloutView, который решает проблему с предоставлением пользовательского представления для выносок и гибкой ширины/высоты довольно безболезненно. Еще некоторые причуды для разработки, но это довольно функционально до сих пор:

https://github.com/u10int/calloutview