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

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

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

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

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

Какие-либо предложения? Я что-то упускаю из виду?

9 ответов

Есть еще более простое решение.

Создать кастом 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...
    }
}

Бум, работа сделана.

detailCalloutAccessoryView

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

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

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

Продолжая блестяще простой ответ @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 (то есть "все"), чтобы они правильно масштабировались в анимации.

:-Джо

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

В вашем 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, тогда вы можете облажаться с ним, и что внутри него.

У меня такая же проблема. В этом блоге есть серьезные посты на эту тему 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 объект и устанавливает свой собственный заголовок, после чего он вызывает два других довольно уродливых метода, которые регулируют ширину и размер содержимого выноски, а затем рисуют речевой пузырь вокруг него в правильном положении.

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

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

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

Я смог достичь этого с помощью: a) установки canShowCallout на NO b) создания подклассов, MKPinAnnotationView и переопределения методов touchesBegan и touchesEnd.

Примечание: вам нужно обрабатывать сенсорные события для MKAnnotationView, а не MKMapView

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

  // 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,

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

https://github.com/u10int/calloutview

Вы можете использовать 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 = @" ";            
Другие вопросы по тегам