Bagaimana cara menyesuaikan gelembung info untuk MKAnnotationView?

88

Saya sedang mengerjakan mapkit dan terjebak.

Saya memiliki tampilan anotasi khusus yang saya gunakan, dan saya ingin menggunakan properti gambar untuk menampilkan titik pada peta dengan ikon saya sendiri. Saya memiliki ini bekerja dengan baik. Tapi yang juga ingin saya lakukan adalah mengganti tampilan info default (gelembung yang muncul dengan judul / subjudul saat ikon anotasi disentuh). Saya ingin dapat mengontrol info itu sendiri: mapkit hanya menyediakan akses ke tampilan info tambahan kiri dan kanan, tetapi tidak ada cara untuk memberikan tampilan kustom untuk gelembung info, atau memberinya ukuran nol, atau apa pun.

Ide saya adalah menimpa selectAnnotation / deselectAnnotation di my MKMapViewDelegate, dan kemudian menggambar tampilan kustom saya sendiri dengan melakukan panggilan ke tampilan anotasi kustom saya. Ini berfungsi, tetapi hanya jika canShowCalloutdisetel ke YESdalam kelas tampilan anotasi kustom saya. Metode ini TIDAK dipanggil jika saya menyetelnya ke NO(yang saya inginkan, sehingga gelembung info default tidak digambar). Jadi saya tidak tahu apakah pengguna menyentuh titik saya di peta (memilihnya) atau menyentuh titik yang bukan bagian dari tampilan anotasi saya (menghapusnya) tanpa memunculkan tampilan gelembung info default.

Saya mencoba melalui jalur yang berbeda dan hanya menangani sendiri semua kejadian sentuh di peta, dan sepertinya saya tidak dapat melakukannya. Saya membaca pos lain yang terkait dengan menangkap peristiwa sentuh di tampilan peta, tetapi itu tidak persis seperti yang saya inginkan. Adakah cara untuk menggali tampilan peta untuk menghilangkan gelembung info sebelum menggambar? Saya bingung.

Ada saran? Apakah saya melewatkan sesuatu yang jelas?

Zach
sumber
Tautan ini tidak berfungsi, tetapi menemukan pos yang sama di sini -> Membuat Keterangan Anotasi Peta Kustom - Bagian 1
Fede Mika
2
Anda dapat merujuk ke proyek ini untuk demo singkat. github.com/akshay1188/CustomAnnotation Kompilasi jawaban di atas menjadi satu demo yang sedang berjalan.
akshay1188

Jawaban:

54

Ada solusi yang lebih mudah.

Buat kustom UIView(untuk info Anda).

Kemudian buat subkelas MKAnnotationViewdan timpa setSelectedsebagai berikut:

- (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, pekerjaan selesai.

TappCandy
sumber
8
hai TappCandy! Pertama, terima kasih atas solusinya. Itu berhasil, tetapi ketika memuat tampilan (saya melakukannya dari file pena) tombol tidak berfungsi. Apa yang dapat saya lakukan untuk membuatnya berfungsi dengan baik ?? Terima kasih
Frade
Suka kesederhanaan solusi ini!
Eric Brotto
6
Hmm - ini tidak berhasil! Ini menggantikan anotasi peta (yaitu pin) BUKAN callout "bubble".
PapillonUK
2
Ini tidak akan berhasil. MKAnnotationView tidak memiliki referensi ke tampilan peta sehingga Anda memiliki beberapa masalah dengan menambahkan info khusus. Misalnya, Anda tidak tahu apakah menambahkan ini sebagai subview akan di luar layar.
Cameron Lowell Palmer
@PapillonUK Anda memiliki kendali atas bingkai callout. Ini tidak menggantikan pin, itu muncul di atasnya. Sesuaikan posisinya saat Anda menambahkannya sebagai subview.
Ben Packard
40

detailCalloutAccessoryView

Di masa lalu ini menyebalkan , tetapi Apple telah menyelesaikannya, cukup periksa dokumen di MKAnnotationView

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

Sungguh, itu dia. Membawa UIView apapun.

Cameron Lowell Palmer
sumber
Mana yang terbaik untuk digunakan?
Satyam
13

Melanjutkan dari jawaban @ TappCandy yang sangat sederhana, jika Anda ingin menganimasikan gelembung Anda dengan cara yang sama seperti yang default, saya telah membuat metode animasi ini:

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

Kelihatannya cukup rumit, tetapi selama titik gelembung info Anda dirancang untuk menjadi tengah-bawah, Anda hanya dapat mengganti myBubbleWidthdan myBubbleHeightdengan ukuran Anda sendiri agar berfungsi. Dan ingat untuk memastikan autoResizeMaskproperti subview Anda telah diatur ke 63 (yaitu "semua") sehingga skalanya dengan benar di animasi.

: -Joe

jowie
sumber
Btw, Anda bisa animasi pada properti skala daripada bingkai, untuk mendapatkan penskalaan konten yang tepat.
occulus
Jangan gunakan CGRectMake. Gunakan CGTransform.
Cameron Lowell Palmer
@occulus - Saya rasa saya mencobanya terlebih dahulu, tetapi mengalami masalah rendering di iOS 4. Tapi itu mungkin karena saya tidak menggunakan CABasicAnimation... Terlalu lama untuk diingat! Saya tahu saya harus menganimasikan properti y juga, karena offset tengah.
jowie
@CameronLowellPalmer - mengapa tidak digunakan CGRectMake?
jowie
@jowie Karena sintaksnya lebih baik, dan menghindari nilai ajaib dalam kode Anda. CGAffineTransformMakeScale (1.1f, 1.1f); Menumbuhkan kotak sebesar 110% sudah jelas. Cara Anda melakukannya mungkin berhasil, tetapi itu tidak bagus.
Cameron Lowell Palmer
8

Menemukan ini sebagai solusi terbaik untuk saya. Anda harus menggunakan beberapa kreativitas untuk melakukan kustomisasi Anda sendiri

Di MKAnnotationViewsubkelas Anda, Anda dapat menggunakan

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

Dan jika subviewadalah a UICalloutView, maka Anda dapat mengacaukannya, dan apa yang ada di dalamnya.

яοвοτағτєяа ււ
sumber
1
bagaimana Anda memeriksa apakah subview adalah UICalloutView karena UICalloutview bukan kelas publik.
Manish Ahuja
if ([[[subview class] description] isEqualToString:@"UICalloutView"]) Saya pikir ada cara yang lebih baik, tapi ini berhasil.
яοвοτағτєяа ււ
apakah Anda memiliki masalah dengan ini karena seperti yang ditunjukkan Manish, ini adalah kelas privat.
Daniel
Mungkin mereka mengubah struktur UIView untuk tampilan panggilan. Saya belum meningkatkan aplikasi yang menggunakan ini, jadi Anda sendirian: P
яοвοτағτєяа ււ
6

Saya memiliki masalah yang sama. Ada banyak postingan blog tentang topik ini di blog ini http://spitzkoff.com/craig/?p=81 .

Hanya menggunakan MKMapViewDelegatetidak membantu Anda di sini dan subclassingMKMapView dan mencoba untuk memperluas fungsionalitas yang ada juga tidak berhasil untuk saya.

Apa yang akhirnya saya lakukan adalah membuat milik saya sendiri CustomCalloutViewyang saya miliki di atas milik sayaMKMapView . Anda dapat mengatur gaya tampilan ini sesuai keinginan Anda.

Saya CustomCalloutViewmemiliki metode yang mirip dengan ini:


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

Dibutuhkan MKAnnotation objek dan menetapkan judulnya sendiri, kemudian memanggil dua metode lain yang cukup jelek yang menyesuaikan lebar dan ukuran konten callout dan kemudian menggambar gelembung ucapan di sekitarnya pada posisi yang benar.

Akhirnya tampilan tersebut ditambahkan sebagai subview ke mapView. Masalah dengan solusi ini adalah sulit untuk menjaga callout pada posisi yang benar saat tampilan peta di-scroll. Saya hanya menyembunyikan callout dalam metode delegasi tampilan peta pada perubahan wilayah untuk memecahkan masalah ini.

Butuh beberapa waktu untuk menyelesaikan semua masalah itu, tetapi sekarang callout hampir berperilaku seperti yang resmi, tetapi saya memilikinya dengan gaya saya sendiri.

Sascha Konietzke
sumber
Saya setuju bahwa: UICalloutView adalah kelas privat dan tidak tersedia di SDK secara resmi. Taruhan terbaik Anda adalah mengikuti saran Sascha.
JoePasq
Hai Sascha, Terima kasih atas balasan Anda. Namun masalah yang saat ini kita alami adalah bagaimana mendapatkan posisi (x, y dan bukan lintang atau bujur) pada peta dimana pin muncul, sehingga kita dapat menampilkan tampilan callout.
Zach
1
mengonversi koordinat dapat dengan mudah dilakukan dengan dua fungsi MKMapView: # - convertCoordinate: toPointToView: # - convertPoint: toCoordinateFromView:
Sascha Konietzke
5

Pada dasarnya untuk mengatasi ini, seseorang perlu: a) Mencegah gelembung info default muncul. b) Cari tahu anotasi mana yang diklik.

Saya dapat mencapai ini dengan: a) pengaturan canShowCallout ke NO b) subclassing, MKPinAnnotationView dan menimpa metode touchesBegan dan touchesEnd.

Catatan: Anda perlu menangani kejadian sentuh untuk MKAnnotationView dan bukan MKMapView

Shivaraj Tenginakai
sumber
3

Saya baru saja datang dengan pendekatan, idenya di sini

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

Di sini - testview UIViewberukuran 320x100 - showCallout adalah BOOL - [self animateIn];adalah fungsi yang menampilkan animasi seperti UIAlertView.

Nikesh K
sumber
Apa yang ditentukan selectedCoordinate? Jenis objek apa itu?
Pradeep Reddy Kypa
itu adalah objek CLLocationCoordinate2d.
bharathi kumar
1

Anda dapat menggunakan leftCalloutView, menyetel annotation.text ke @ ""

Silakan temukan di bawah kode contoh:

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 = @" ";            
Fabio Napodano
sumber
1
Ini akan 'bekerja' sampai batas tertentu, tetapi tidak benar-benar menjawab pertanyaan yang lebih umum yang diajukan dan itu sangat hackish.
Cameron Lowell Palmer
1

Saya telah mendorong garpu saya dari SMCalloutView yang sangat baik yang memecahkan masalah dengan menyediakan tampilan khusus untuk info dan memungkinkan lebar / tinggi yang fleksibel tanpa rasa sakit. Masih ada beberapa keanehan untuk dikerjakan, tetapi sejauh ini cukup berfungsi:

https://github.com/u10int/calloutview

u10int
sumber