Як налаштувати спливаючу підказку для MKAnnotationView?


88

Зараз я працюю з mapkit і застряг.

У мене є власний вигляд анотацій, який я використовую, і я хочу використовувати властивість image для відображення точки на карті з власною піктограмою. У мене це працює нормально. Але що я також хотів би зробити, це замінити подання виноски за замовчуванням (спливаюча підказка, яка відображається із заголовком / підзаголовком, коли торкаємося значка анотації). Я хочу мати можливість керувати самою виноскою: mapkit надає доступ лише до лівого та правого допоміжних виглядів виноски, але ніяк не можна надати власний вигляд для виноски виноски, або надати йому нульовий розмір, або щось інше.

Моя ідея полягала в тому, щоб замінити selectAnnotation / deselectAnnotation у моєму MKMapViewDelegate, а потім намалювати власний власний вигляд, зробивши виклик до мого користувацького подання анотацій. Це працює, але тільки тоді , коли canShowCalloutбуде встановлено YESв моєму призначеному для користувача класі перегляду анотацій. Ці методи НЕ викликаються, якщо для мене встановлено цей параметр NO(саме цього я і хочу, щоб мітка з виносками за замовчуванням не була намальована). Отже, я ніяк не можу дізнатися, чи користувач торкнувся моєї точки на карті (вибрав її) чи торкнувся точки, яка не є частиною моїх переглядів анотацій (видалила її), не з’явившись перегляду міхурів за умовчанням.

Я спробував піти іншим шляхом і просто обробляти всі події дотику сам на карті, і, здається, я не можу це зробити. Я читав інші публікації, пов’язані з ловом дотикових подій у поданні карти, але вони не зовсім такі, як я хочу. Чи є спосіб заглибитися у вигляд карти, щоб видалити виноску з виносками перед нанесенням малюнка? Я в розгубі.

Будь-які пропозиції? Мені не вистачає чогось очевидного?


Це посилання не працює, але знайшов ту саму публікацію тут -> Створення власних анотацій анотацій карт - Частина 1
Fede Mika

2
Ви можете звернутися до цього проекту для швидкої демонстрації. github.com/akshay1188/CustomAnnotation Компіляція відповідей вище в одній запущеній демонстрації.
akshay1188

Відповіді:


53

Є ще простіше рішення.

Створіть власний 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...
    }
}

Бум, виконана робота.


8
привіт TappCandy! По-перше, дякую за ваше рішення. Це справді працює, але при завантаженні подання (я роблю це з файлу nib) кнопки не працюють. Що я можу зробити, щоб вони працювали нормально ?? Дякую
Фрейд

Полюбіть простоту цього рішення!
Ерік Бротто,

6
Хм - це не працює! Він замінює анотацію на карті (тобто шпильку), а НЕ "міхур" виноски.
PapillonUK

2
Це не спрацює. MKAnnotationView не має посилання на перегляд карти, тому у вас є кілька проблем із додаванням власної виноски. Наприклад, ви не знаєте, чи додавати це як підпрогляд буде поза екраном.
Камерон Лоуелл Палмер,

@PapillonUK Ви маєте контроль над рамкою виноски. Він не замінює штифт, він з’являється поверх нього. Налаштуйте його положення, коли додаєте його як підпрогляд.
Ben Packard

40

detailCalloutAccessoryView

За старих часів це було болем, але Apple це вирішила, просто перевірте документи на MKAnnotationView

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

Справді, все. Бере будь-який UIView.


Який з них найкраще використовувати?
Satyam

13

Продовжуючи блискуче просту відповідь @ 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 (тобто «всі»), щоб вони правильно масштабувались в анімації.

: -Джо


До речі, ви можете анімувати властивість масштабу, а не кадру, щоб отримати належне масштабування вмісту.
okulus

Не використовуйте CGRectMake. Використовуйте CGTransform.
Камерон Лоуелл Палмер,

@occulus - Думаю, я спробував це спочатку, але мав проблеми з візуалізацією в iOS 4. Але це могло бути через те, що я не використовував CABasicAnimation... Занадто давно, щоб пам’ятати! Я знаю, що мені також довелося б анімувати y властивість, через зміщення центру.
jowie

@CameronLowellPalmer - чому б не використовувати CGRectMake?
jowie

@jowie Оскільки синтаксис кращий і уникає магічних значень у вашому коді. CGAffineTransformMakeScale (1.1f, 1.1f); Зрости ящик на 110% зрозуміло. Те, як ви це робили, може спрацювати, але це не красиво.
Камерон Лоуелл Палмер,

8

Виявив, що це найкраще рішення для мене. Вам доведеться використати певний креатив, щоб зробити власні налаштування

У своєму 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це a UICalloutView, то ви можете з ним покрутитися, і що там всередині.


1
як перевірити, чи є підвигляд UICalloutView, оскільки UICalloutview не є загальнодоступним класом.
Manish Ahuja

if ([[[subview class] description] isEqualToString:@"UICalloutView"]) Я думаю, що є кращий спосіб, але це працює.
яовοτағτєяа ււ

у вас були якісь проблеми з цим, оскільки, як зазначив Маніш, це приватний клас.
Даніель

Можливо, вони змінили структуру UIView для переглядів виносок. Я не модернізував програму, яка цим користується, тож ти сам по собі: P
яовοτағτєяа ււ

6

У мене була та сама проблема. У цьому щоденнику 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. Проблема цього рішення полягає в тому, що важко утримати виноску у правильному положенні, коли прокручується вигляд карти. Я просто приховую виноску в методі делегування переглядів мапи при зміні регіону, щоб вирішити цю проблему.

Щоб вирішити всі ці проблеми, знадобився певний час, але зараз виноска майже поводиться як офіційна, але у мене це у своєму стилі.


Я повторюю це: UICalloutView є приватним класом і офіційно недоступний у SDK. Найкраще - дотримуватися порад Саші.
JoePasq

Привіт Саша, ми вдячні Вам за Вашу відповідь. Але поточна проблема, з якою ми стикаємось, полягає в тому, як отримати позицію (x, y, а не широту чи довжину) на карті, де з’являються шпильки, щоб ми могли відобразити вигляд виноски.
Зак

1
перетворення координат можна легко виконати за допомогою двох функцій MKMapView: # - convertCoordinate: toPointToView: # - convertPoint: toCoordinateFromView:
Саша Коніцке

5

В основному, щоб вирішити цю проблему, потрібно: а) Не допустити появи спливаючої підказки за замовчуванням. б) З’ясуйте, яку анотацію клацнули.

Мені вдалося досягти цього, а) встановивши для canShowCallout значення NO; б) підкласифікацію, MKPinAnnotationView та перевизначення методів touchBegan та touchesEnd.

Примітка: Вам потрібно обробляти події дотику для MKAnnotationView, а не MKMapView


3

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

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


Що вказує selectedCoordinate? Що це за Об'єкт?
Pradeep Reddy Kypa

це об’єкт CLLocationCoordinate2d.
bharathi kumar

1

Ви можете використовувати leftCalloutView, встановивши для анотації.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 = @" ";            

1
Це певною мірою "спрацює", але насправді не відповідає на загальніше запитання, і це дуже хакі.
Камерон Лоуелл Палмер,

1

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

https://github.com/u10int/calloutview

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.