MKAnnotationView için açıklama balonu nasıl özelleştirilir?


88

Şu anda harita seti ile çalışıyorum ve takılı kaldım.

Kullanmakta olduğum özel bir açıklama görünümüne sahibim ve haritadaki noktayı kendi simgemle görüntülemek için image özelliğini kullanmak istiyorum. Bu iyi çalışıyor. Ancak benim de yapmak istediğim şey, varsayılan belirtme çizgisi görünümünü (ek açıklama simgesine dokunduğunuzda başlık / alt başlık ile birlikte görünen balon) geçersiz kılmaktır. Belirtme çizgisinin kendisini kontrol edebilmek istiyorum: harita seti yalnızca sol ve sağ yardımcı belirtme çizgisi görünümlerine erişim sağlar, ancak belirtme çizgisi balonu için özel bir görünüm veya sıfır boyut veya başka bir şey sağlamanın yolu yoktur.

Benim fikrim, benim içinde selectAnnotation / deselectAnnotation'ı geçersiz kılmak MKMapViewDelegateve ardından özel açıklama görünümüme bir çağrı yaparak kendi özel görünümümü çizmekti. Bu işe yarar, ancak yalnızca özel ek açıklama görünümü sınıfımda canShowCalloutolarak ayarlandığında YES. Bunu ayarladıysam bu yöntemler çağrılmaz NO(istediğim şey budur, böylece varsayılan belirtme balonu çizilmez). Bu nedenle, kullanıcının haritadaki noktama dokunduğunu (seçtiğini) veya ek açıklama görünümlerimin parçası olmayan bir noktaya dokunduğunu (silindi), varsayılan belirtme çizgisi balon görünümü görünmeden bilmem mümkün değil.

Farklı bir yoldan gitmeyi ve haritadaki tüm dokunma olaylarını kendim halletmeyi denedim ve bu işe yaramıyor gibi görünüyor. Harita görünümünde dokunma olaylarını yakalamayla ilgili diğer gönderileri okudum, ancak tam olarak istediğim şey değiller. Çizmeden önce açıklama balonunu kaldırmak için harita görünümüne girmenin bir yolu var mı? Kaybettim.

Herhangi bir öneri? Bariz bir şeyi mi kaçırıyorum?


Bu bağlantı çalışmıyor, ancak burada aynı gönderiyi
Fede Mika

2
Hızlı bir demo için bu projeye başvurabilirsiniz . github.com/akshay1188/CustomAnnotation Yukarıdaki yanıtların tek bir çalışan demoda derlenmesi.
akshay1188

Yanıtlar:


54

Daha da kolay bir çözüm var.

Bir özel oluşturun UIView(belirtme çizginiz için).

Ardından aşağıdaki gibi bir alt sınıf oluşturun MKAnnotationViewve geçersiz kılın 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...
    }
}

Boom, iş bitti.


8
merhaba TappCandy! Öncelikle çözümünüz için teşekkürler. İşe yarıyor, ancak bir görünüm yüklendiğinde (bunu bir uç dosyasından yapıyorum) düğmeler çalışmıyor. Onların düzgün çalışmasını sağlamak için ne yapabilirim? Teşekkürler
Frade

Bu çözümün basitliğini seviyorum!
Eric Brotto

6
Hmm - bu işe yaramıyor! Harita bilgi notunun (yani raptiyenin) yerini alır, belirtme çizgisi "balonu" DEĞİL.
PapillonUK

2
Bu işe yaramayacak. MKAnnotationView, harita görünümüne bir başvuruda bulunmadığından, özel bir belirtme çizgisi eklemeyle ilgili çeşitli sorunlarınız vardır. Örneğin, bunu bir alt görünüm olarak eklemenin ekran dışı olup olmayacağını bilmiyorsunuz.
Cameron Lowell Palmer

@PapillonUK Belirtme çizgisinin çerçevesi üzerinde kontrole sahipsiniz. Pimi değiştirmiyor, üstünde görünüyor. Alt görünüm olarak eklediğinizde konumunu ayarlayın.
Ben Packard

40

detailCalloutAccessoryView

Eski günlerde bu bir acıydı , ancak Apple bunu çözdü, sadece MKAnnotationView'daki dokümanları kontrol edin

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

Gerçekten, işte bu. Herhangi bir UIView alır.


Hangisini kullanmak en iyisidir?
Satyam

13

@ TappCandy'nin zekice basit cevabından devam edersek, balonunuzu varsayılanla aynı şekilde canlandırmak istiyorsanız, şu animasyon yöntemini ürettim:

- (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);
            }];
        }];
    }];
}

Oldukça karmaşık görünüyor, ama sürece çizgisi balonunun noktası merkez alt olacak şekilde tasarlanmıştır olarak, sadece değiştirmek gerekir myBubbleWidthve myBubbleHeightişe bunun için kendi boyutu ile. Ve alt görünümlerinizin özelliklerinin autoResizeMask63'e (yani "tümü") ayarlandığından emin olun, böylece animasyonda doğru şekilde ölçeklenebilsinler.

: -Joe


Btw, içeriğin doğru ölçeklendirilmesini elde etmek için çerçeve yerine ölçek özelliğinde animasyon uygulayabilirsiniz.
occulus

CGRectMake kullanmayın. CGTransform'u kullanın.
Cameron Lowell Palmer

@occulus - Sanırım önce bunu denedim, ancak iOS 4'te render sorunları yaşadım. Ancak bu, kullanmadığım için olabilirdi CABasicAnimation... Hatırlamak için çok uzun zaman önce! Merkez ofset nedeniyle y özelliğini de canlandırmam gerektiğini biliyorum.
jowie

@CameronLowellPalmer - neden kullanılmıyor CGRectMake?
jowie

@jowie Çünkü sözdizimi daha iyidir ve kodunuzdaki sihirli değerlerden kaçınır. CGAffineTransformMakeScale (1.1f, 1.1f); Kutuyu% 110 büyütmek açıktır. Bunu yapma şeklin işe yarayabilir, ama hoş değil.
Cameron Lowell Palmer

8

Bunu benim için en iyi çözüm olarak buldum. Kendi özelleştirmelerinizi yapmak için biraz yaratıcılık kullanmanız gerekecek

Senin içinde MKAnnotationViewalt sınıf, kullanabilirsiniz

- (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++;
            }
        }
    }
}

Ve eğer subviewa ise UICalloutView, onunla ve içinde ne varsa onunla uğraşabilirsiniz.


1
UICalloutview genel bir sınıf olmadığından, alt görünümün bir UICalloutView olup olmadığını nasıl kontrol edersiniz?
Manish Ahuja

if ([[[subview class] description] isEqualToString:@"UICalloutView"]) Bence daha iyi bir yol var ama bu işe yarıyor.
яοвοτағτєяа ււ

Manish'in de belirttiği gibi, bu özel bir sınıf olduğu için bununla ilgili herhangi bir sorununuz oldu mu?
Daniel

Muhtemelen belirtme çizgileri için UIView yapısını değiştirdiler. Bunu kullanan uygulamayı yükseltmedim, bu yüzden kendi başınasın: P
яοвοτағτєяа ււ

6

Ben de aynı sorunu yaşadım. Bu blogda http://spitzkoff.com/craig/?p=81 bu konuyla ilgili ciddi blog yazıları var .

Sadece burada kullanmak MKMapViewDelegatesize yardımcı olmaz ve alt sınıflandırma yapmak MKMapViewve mevcut işlevselliği genişletmeye çalışmak da benim için işe yaramadı.

Sonunda yaptığım şey, kendi CustomCalloutViewkendime sahip olduğum şeyi yaratmaktı MKMapView. Bu görünümü istediğiniz şekilde şekillendirebilirsiniz.

Benim CustomCalloutViewbuna benzer bir yöntem var:


- (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];
}

Bir alır MKAnnotation nesneyi ve kendi başlığını belirler, daha sonra belirtme çizgisi içeriklerinin genişliğini ve boyutunu ayarlayan oldukça çirkin olan ve daha sonra konuşma balonunu doğru konumda etrafına çeken diğer iki yöntemi çağırır.

Son olarak görünüm, mapView'e bir alt görünüm olarak eklenir. Bu çözümdeki sorun, harita görünümü kaydırıldığında belirtme çizgisini doğru konumda tutmanın zor olmasıdır. Bu sorunu çözmek için bir bölge değişikliğinde harita görünümlerinde temsilci yönteminde belirtme çizgisini gizliyorum.

Tüm bu sorunları çözmek biraz zaman aldı, ancak şimdi belirtme çizgisi neredeyse resmi olan gibi davranıyor, ancak kendi tarzıma sahibim.


İkinci olarak: UICalloutView özel bir sınıftır ve SDK'da resmi olarak mevcut değildir. En iyi bahsiniz, Sascha'nın tavsiyesine uymaktır.
JoePasq

Merhaba Sascha, Cevabınız için teşekkür ederiz. Ancak, yaşadığımız mevcut sorun, harita üzerinde pimlerin göründüğü konumu (x, y ve enlem veya uzun değil) nasıl elde edeceğimizdir, böylece belirtme çizgisi görünümünü görüntüleyebiliriz.
Zach

1
koordinatları dönüştürmek MKMapView'ın iki işlevi ile kolayca yapılabilir: # - convertCoordinate: toPointToView: # - convertPoint: toCoordinateFromView:
Sascha Konietzke

5

Temel olarak bunu çözmek için aşağıdakilerin yapılması gerekir: a) Varsayılan belirtme çizgisi balonunun ortaya çıkmasını önlemek. b) Hangi ek açıklamanın tıklandığını bulun.

Bunları şunlarla başardım: a) canShowCallout'u NO olarak ayarlayarak b) alt sınıflandırma, MKPinAnnotationView ve touchesBegan ve touchesEnd yöntemlerini geçersiz kılarak.

Not: Dokunma olaylarını MKAnnotationView için işlemeniz gerekir, MKMapView için değil


3

Sadece bir yaklaşım buldum, buradaki fikir

  // 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];
    }
}

Burada - testview UIView320x100 boyutunda bir - showCallout BOOL'dur - [self animateIn];animasyonu benzer şekilde görüntüleyen işlevdir UIAlertView.


SelectedCoordinate neyi belirtir? Bu ne tür bir Nesne?
Pradeep Reddy Kypa

CLLocationCoordinate2d nesnesidir.
bharathi kumar

1

Annotation.text'i @ "" olarak ayarlayarak leftCalloutView'ı kullanabilirsiniz

Lütfen aşağıdaki örnek kodu bulun:

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
Bu bir dereceye kadar 'işe yarayacaktır', ancak sorulan daha genel soruyu gerçekten yanıtlamaz ve oldukça zordur.
Cameron Lowell Palmer

1

Belirtme çizgileri için özel bir görünüm sağlayarak ve esnek genişliklere / yüksekliklere oldukça acısız bir şekilde izin vererek sorunu çözen mükemmel SMCalloutView çatalımı çıkardım. Hala bazı tuhaflıklar var, ancak şu ana kadar oldukça işlevsel:

https://github.com/u10int/calloutview

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.